SpringMVC是一款基于MVC架构模式的轻量级Web框架,其目的是将Web开发模块化,对整体架构进行解耦,简化Web开发流程。SpringMVC基于请求驱动,即使用请求-响应模型。由于SpringMVC遵循MVC架构规范,因此分层开发数据模型层(Model)、响应视图层(View)和控制层(Controller),可以让开发者设计出结构规整的Web层。
概念
SpringMVC中的核心DispatcherServlet继承了HttpServlet,并且重写了Java中HttpServlet的方法,用于处理get/post请求,其中重写了init方法用来解析web.xml的配置,解析SpringMVC的xml配置,如扫描包的路径,然后根据这个路径扫描整个项目,确定controller的位置,利用@RequestMapping注解拦截请求,并调用controller的处理方法,返回需要的值。
模拟DispatcherServlet
DispatcherServlet重写HttpServlet的init方法,首先利用getInitParameter方法获取到xml文件中的位置,然后进行xml文件的配置解析,利用dom4j的api。首先引入dom4j,在pom.xml文件中添加依赖。
1 2 3 4 5 6 7
|
<dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency>
|
SpringMVC.xml文件放在resources文件夹下,xml文件解析的步骤:
- 创建SAXReader对象
- 利用read方法读取xml文件
- 获取xml文件的根节点
- 利用根节点拿到子节点
- 获取子节点中的元素
- 拿到了扫描项目的入口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
| package com.jinqi.servlet;
import com.alibaba.fastjson.JSON; import com.jinqi.annotation.Controller; import com.jinqi.annotation.RequestMapping; import com.jinqi.annotation.ResponseBody; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader;
import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.HashMap; import java.util.Map;
public class TestDispatcherServlet extends HttpServlet {
private static String CLASS_PATH = TestDispatcherServlet.class.getResource("/").getPath();
private static String SCAN_PATH = "";
private static String BASE_PATH;
private static Map<String,Method> map = new HashMap<>();
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); }
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try{ String requestURI = req.getRequestURI(); Method method = map.get(requestURI); if (method!=null){ Class<?> clazz = method.getDeclaringClass(); Object o = clazz.newInstance(); Parameter[] parameters = method.getParameters(); Object[] objects = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { String paraName = parameters[i].getName(); Class paraType = parameters[i].getType(); if (paraType==HttpServletRequest.class){ objects[i] = req; }else if (paraType==HttpServletResponse.class){ objects[i] = resp; }else if (paraType==String.class){ String parameter = req.getParameter(paraName); objects[i] = parameter; }else { Object o1 = paraType.newInstance(); for (Field field : paraType.getDeclaredFields()) { field.setAccessible(true); String name = field.getName(); String parameter = req.getParameter(name); field.set(o1,parameter); } objects[i] = o1; } } Object invoke = method.invoke(o, objects); if (method.getAnnotation(ResponseBody.class)!=null){ resp.getWriter().write(JSON.toJSONString(invoke)); }else { if (method.getReturnType()==String.class) { req.getRequestDispatcher("/"+(String) invoke).forward(req,resp); } } }else { resp.setStatus(404); } }catch (Exception e){ e.printStackTrace(); } }
@Override public void init(ServletConfig config) throws ServletException {
try{ String mvcLocation = config.getInitParameter("testContextConfigLocation"); CLASS_PATH = CLASS_PATH.replaceAll("%20"," "); File xmlFile = new File(CLASS_PATH+mvcLocation); SAXReader saxReader = new SAXReader(); Document xmlDocument = saxReader.read(xmlFile); Element rootElement = xmlDocument.getRootElement(); Element packageScan = rootElement.element("packageScan"); Attribute aPackage = packageScan.attribute("package"); SCAN_PATH = aPackage.getValue(); File file = new File(CLASS_PATH+SCAN_PATH); BASE_PATH = file.getPath(); scanPackage(file);
}catch (Exception e){ e.printStackTrace(); } }
public void scanPackage(File file){ try { if(file.isDirectory()){ for (File file1 : file.listFiles()) { scanPackage(file1); } }else { String fileName = file.getName(); if (fileName.substring(fileName.lastIndexOf(".")).equals(".class")){ String filePath = file.getPath(); filePath = filePath.replace(BASE_PATH,""); filePath = SCAN_PATH + filePath; String classPath = filePath.replaceAll("\\\\","."); String className = classPath.substring(0,classPath.lastIndexOf(".")); Class<?> clazz = Class.forName(className); if (clazz.isAnnotationPresent(Controller.class)){ RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class); String url_path = ""; if (classRequestMapping!=null){ url_path = classRequestMapping.value(); } for (Method method : clazz.getDeclaredMethods()) { String methodPath = ""; RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class); if (methodRequestMapping!=null){ methodPath = methodRequestMapping.value(); map.put(url_path+methodPath,method); System.out.println(url_path+methodPath+"被映射到了"+clazz.getName()+"的"+method.getName()+"方法上"); } } } } } }catch (Exception e){ e.printStackTrace(); } } }
|
编写controller进行测试
通常我们写Spring项目时,会在controller类上加上两个注解:@Controller和@RequestMapping。表明这是controller控制器和拦截的请求路径,因此我们也手写这两个注解用于测试。
1 2 3 4 5 6 7 8 9 10 11 12
| package com.jinqi.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface RequestMapping { String value() default " "; }
|
1 2 3 4 5 6 7 8 9 10 11
| package com.jinqi.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Controller { }
|
controller类我们写getUser方法并传入HttpServletRequest、HttpServletResponse对象,以及姓名和User实体类对象,并将其打印,看能否获得正确的结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.jinqi.controller;
import com.jinqi.annotation.Controller; import com.jinqi.annotation.RequestMapping; import com.jinqi.annotation.ResponseBody; import com.jinqi.entity.UserEntity;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
@Controller @RequestMapping("/user") public class UserController {
@RequestMapping("/getUser.do") @ResponseBody public Object getUser(HttpServletRequest request, HttpServletResponse response, String name, UserEntity userEntity){
System.out.println(request); System.out.println(response); System.out.println(name); System.out.println(userEntity); System.out.println("getUser"); return userEntity; }
@RequestMapping("/index.do") public String index(){ return "index.html"; } }
|