1.SpringMVC的工作流程

SpringMVC - 图1

  • 客户发送请求 (request)
  • 请求交由核心处理(DispatcherServlet)
  • 核心控制器找到映射器,映射器看看请求路径是什么(HandleMapping)
  • 控制器再找到适配器,看看有哪些类实现了Controller接口或者对应的Bean对象(HandleAdapter)
  • 将带过来的数据进行转换,格式化等等操作 找到我们的控制器Action,处理完业务之后返回
  • 最后通过视图解析器来对ModelAndView进行解析
  • 跳转到对应的JSP/html页面

    2.核心代码

    2.1 在web.xml中配置使得controller的请求都经过DispatcherServlet

    image.png

    2.2DispatcherServlet

    ```java package com.woniuxy.myspringmvc.core;

import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Properties; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;

import com.fasterxml.jackson.databind.ObjectMapper; import com.woniuxy.myspringmvc.annotation.ResponseBody; import com.woniuxy.myspringmvc.annotation.ResponseForward; import com.woniuxy.myspringmvc.annotation.ResponseRedirect;

public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; // 配置文件 private Properties configProp = new Properties(); // 获取包下controller类全名 private List classNames = new ArrayList(); private HandlerMapper handlerMapper; private HandlerAdapter handlerAdapter; public void init(ServletConfig config) throws ServletException { // 初始化 System.out.println(“初始化”); // 加载配置文件,读取内容 loadProperties(config); // 扫描指定包下的类,获取该包下的所有类全名,并去存储到classNames中 doScanner(configProp.getProperty(“package”)); //创建处理器映射对象 handlerMapper=new HandlerMapper(classNames); //创建处理器适配器对象 handlerAdapter=new HandlerAdapter(handlerMapper); }

  1. private void doScanner(String packageName) {
  2. // 类全名:包名+类名
  3. // 将包名转化为文件路径
  4. String packagePath = packageName.replaceAll("\\.", "/");
  5. // 获取src文件下的真实路径,widow中空格是%20,要将他转换成空格
  6. String path = DispatcherServlet.class.getClassLoader().getResource(packagePath).getPath().replace("%20", " ");
  7. // 创建file对象
  8. File file = new File(path);
  9. // 判断文件是否存在
  10. if (!file.exists()) {
  11. return;
  12. }
  13. // 获取子文件
  14. File[] files = file.listFiles();
  15. for (File f : files) {
  16. if (f.isDirectory()) {
  17. doScanner(packageName + "." + f.getName());
  18. } else {
  19. // 获取源文件名称
  20. String fileName = f.getName();
  21. // 获取类名
  22. String name = fileName.replace(".class", "");
  23. // 类全名
  24. String className = packageName + "." + name;
  25. // 存储到controllers中
  26. //System.out.println(className);
  27. classNames.add(className);
  28. }
  29. }
  30. System.out.println(path);
  31. }
  32. private void loadProperties(ServletConfig config) {
  33. // 根据name获取servlet初始化参数值
  34. String configName = config.getInitParameter("config");
  35. // 如果用户配置了文件名,则以配置的为准,否则取默认值
  36. if (configName == null) {
  37. configName = "application.properties";
  38. }
  39. InputStream is = null;
  40. try {
  41. is = DispatcherServlet.class.getClassLoader().getResourceAsStream(configName);
  42. configProp.load(is);
  43. } catch (Exception e) {
  44. e.printStackTrace();
  45. } finally {
  46. try {
  47. if (is != null)
  48. is.close();
  49. } catch (IOException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. }
  54. protected void service(HttpServletRequest request, HttpServletResponse response)
  55. throws ServletException, IOException {
  56. Object result = handlerAdapter.adapter(request, response);
  57. if (result==null) {
  58. return;
  59. }
  60. //获取当前方法对象
  61. Method method=handlerMapper.getMethodMapper().get(request.getPathInfo());
  62. if (method.isAnnotationPresent(ResponseBody.class)) {
  63. //设置响应内容类型
  64. response.setContentType("application/json;charset=utf-8");
  65. //将返回值的java对象转化成json,并通过指定输出流写出
  66. new ObjectMapper().writeValue(response.getOutputStream(),result);
  67. }else if(method.isAnnotationPresent(ResponseForward.class)){
  68. String path=(String)result;
  69. //请求转发
  70. request.getRequestDispatcher("../../"+path).forward(request, response);
  71. }else if(method.isAnnotationPresent(ResponseRedirect.class)){
  72. String path=(String)result;
  73. //重定向
  74. response.sendRedirect(path);
  75. }
  76. }

}

  1. <a name="6zImK"></a>
  2. ## 2.3HandlerMapping
  3. ```java
  4. package com.woniuxy.myspringmvc.core;
  5. import java.lang.reflect.Method;
  6. import java.util.HashMap;
  7. import java.util.List;
  8. import java.util.Map;
  9. import com.woniuxy.myspringmvc.annotation.Controller;
  10. import com.woniuxy.myspringmvc.annotation.RequestMapping;
  11. public class HandlerMapper {
  12. // 存储controller的实例,键:类名,值:对应controller对象
  13. private Map<String, Object> controllers = new HashMap<String, Object>();
  14. // 存储请求URL与controller映射,键:请求URL,值controller对象
  15. private Map<String, Object> controllerMapper = new HashMap<String, Object>();
  16. // 存储请求URL与controller映射,键:请求URL,值controller对象的方法
  17. private Map<String, Method> methodMapper = new HashMap<String, Method>();
  18. public HandlerMapper(List<String> classNames) {
  19. // 实例化:创建添加了controller注解的controller对象,并存储到controllers中
  20. doInstance(classNames);
  21. // 建立映射,建立请求与controller的方法映射
  22. initHandlerMapper();
  23. }
  24. private void initHandlerMapper() {
  25. // 循环遍历每个controller对象
  26. for (Object controller : controllers.values()) {
  27. // 获取当前controller对象的class实例;
  28. Class cl = controller.getClass();
  29. // 判断当前controller类上是否存在RequestMapping注解
  30. if (cl.isAnnotationPresent(RequestMapping.class)) {
  31. // 获取requestmapping注解
  32. RequestMapping controllerRequestMapping = (RequestMapping) cl
  33. .getDeclaredAnnotation(RequestMapping.class);
  34. // 获取注解值
  35. String controllerRequestMappingValue = controllerRequestMapping.value();
  36. // 获取controller类所有方法
  37. Method[] methods = cl.getDeclaredMethods();
  38. for (Method method : methods) {
  39. // 判断当前方法是否存在requestmapping注解
  40. if (method.isAnnotationPresent(RequestMapping.class)) {
  41. // 获取当前方法的requestmapping注解
  42. RequestMapping methodRequestMapping = method.getDeclaredAnnotation(RequestMapping.class);
  43. // 获取当前方法上的request1mapping注解值
  44. String methodRequestMappingValue = methodRequestMapping.value();
  45. // 生成键
  46. String key = controllerRequestMappingValue + methodRequestMappingValue;
  47. // 建立请求与controller的映射
  48. controllerMapper.put(key, controller);
  49. // 建立请求与method的映射
  50. methodMapper.put(key, method);
  51. }
  52. }
  53. }
  54. }
  55. }
  56. private void doInstance(List<String> classNames) {
  57. // 循环遍历每个controller全类名
  58. for (String className : classNames) {
  59. try {
  60. // 通过类全名获取class实例
  61. Class cl = Class.forName(className);
  62. // 判断当前controller上是否存在controlle注解
  63. if (cl.isAnnotationPresent(Controller.class)) {
  64. // 创建目标controller对象
  65. Object o = cl.newInstance();
  66. // 存储到controllers中
  67. controllers.put(cl.getSimpleName(), o);
  68. }
  69. } catch (Exception e) {
  70. e.printStackTrace();
  71. }
  72. }
  73. }
  74. public Map<String, Object> getControllers() {
  75. return controllers;
  76. }
  77. public void setControllers(Map<String, Object> controllers) {
  78. this.controllers = controllers;
  79. }
  80. public Map<String, Object> getControllerMapper() {
  81. return controllerMapper;
  82. }
  83. public void setControllerMapper(Map<String, Object> controllerMapper) {
  84. this.controllerMapper = controllerMapper;
  85. }
  86. public Map<String, Method> getMethodMapper() {
  87. return methodMapper;
  88. }
  89. public void setMethodMapper(Map<String, Method> methodMapper) {
  90. this.methodMapper = methodMapper;
  91. }
  92. }

2.4HandlerAdapter

  1. package com.woniuxy.myspringmvc.core;
  2. import java.io.IOException;
  3. import java.lang.reflect.Field;
  4. import java.lang.reflect.Method;
  5. import java.lang.reflect.Parameter;
  6. import java.util.Iterator;
  7. import java.util.Map;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. public class HandlerAdapter {
  11. private HandlerMapper handlerMapper;
  12. public HandlerAdapter(HandlerMapper handlerMapper) {
  13. this.handlerMapper = handlerMapper;
  14. }
  15. public Object adapter(HttpServletRequest request, HttpServletResponse response) throws IOException {
  16. Object result = null;
  17. // 获取请求的映射键
  18. String key = request.getPathInfo();
  19. // 根据键查找对应controller对象以及method对象
  20. Object controller = handlerMapper.getControllerMapper().get(key);
  21. Method method = handlerMapper.getMethodMapper().get(key);
  22. if (controller == null | method == null) {
  23. response.sendError(404, "请求的controller或method不存在!");
  24. return result;
  25. }
  26. // 获取参数个数
  27. int methodParamCount = method.getParameterCount();
  28. // 存储方法参数实参
  29. Object[] realMethodParams = new Object[methodParamCount];
  30. // 获取页面所有参数的map对象
  31. Map<String, String[]> pageParamVals = request.getParameterMap();
  32. // 获取方法所有参数
  33. Parameter[] methodParames = method.getParameters();
  34. for (int i = 0; i < methodParames.length; i++) {
  35. // 每一个形参
  36. Parameter methodParam = methodParames[i];
  37. // 获取方法参数类型
  38. Class methodParamTypeCl = methodParam.getType();
  39. // 获取参数方法名称
  40. String methodTypeName = methodParamTypeCl.getSimpleName();
  41. // 获取方法参数名称
  42. String methodParamName = methodParam.getName();
  43. // 判断参数数据类型
  44. if (methodTypeName.equals("HttpServletRequest")) {
  45. realMethodParams[i] = request;
  46. } else if (methodTypeName.equals("HttpServletResponse")) {
  47. realMethodParams[i] = response;
  48. } else if (methodParamTypeCl.getClassLoader() == null) {
  49. // java自带类型,比如String、int等
  50. // 取出当前方法的参数的实参
  51. String[] pageParamVal = pageParamVals.get(methodParamName);
  52. // 判断参数是否存在
  53. if (pageParamVal == null) {
  54. response.sendError(404, method.getName() + "方法的" + methodParamName + "参数不存在!");
  55. return result;
  56. }
  57. // 将客户端传参进行类型转换
  58. realMethodParams[i] = parseType(pageParamVal, methodParamTypeCl);
  59. } else {
  60. // 自定义类型
  61. Object o = null;
  62. try {
  63. // 创建目标类对象
  64. o = methodParamTypeCl.newInstance();
  65. // 获取当前类的属性
  66. Field[] files = methodParamTypeCl.getDeclaredFields();
  67. for (Field field : files) {
  68. // 获取当前属性名称
  69. String fieldName = field.getName();
  70. // 根据属性名和方法参数名称获取前端参数键
  71. String pageParamKey = methodParamName + "." + fieldName;
  72. // 尝试从前端参数中取值
  73. String[] pageParamVal = pageParamVals.get(pageParamKey);
  74. if (pageParamVal == null) {
  75. continue;
  76. }
  77. // 获取当前属性类型
  78. Class fieldTypeCl = field.getType();
  79. // 将String转为真实类型
  80. Object val = parseType(pageParamVal, fieldTypeCl);
  81. // 赋值
  82. // 获取set方法名称
  83. String fieldSetMethodName = "set" + (fieldName.charAt(0) + "").toUpperCase()
  84. + fieldName.substring(1);
  85. // 获取set方法对象
  86. Method fieldSetMethod = methodParamTypeCl.getDeclaredMethod(fieldSetMethodName, fieldTypeCl);
  87. // 执行set方法,赋值
  88. fieldSetMethod.invoke(o, val);
  89. }
  90. } catch (Exception e) {
  91. e.printStackTrace();
  92. }
  93. realMethodParams[i] = o;
  94. }
  95. }
  96. try {
  97. result = method.invoke(controller, realMethodParams);
  98. } catch (Exception e) {
  99. e.printStackTrace();
  100. }
  101. return result;
  102. }
  103. private Object parseType(String[] pageParamVal, Class methodTypeCl) {
  104. Object val = null;
  105. // 存储当前目标类型是否为数组类型
  106. boolean isArray = false;
  107. // 存储元素类型名称
  108. String aimTypeName = null;
  109. // 判断目标类型是否为数组
  110. if (methodTypeCl.isArray()) {
  111. isArray = true;
  112. aimTypeName = methodTypeCl.getComponentType().getSimpleName();
  113. } else {
  114. aimTypeName = methodTypeCl.getSimpleName();
  115. }
  116. // 分别判断目标类型,进行类型转换
  117. if (aimTypeName.equals("String")) {
  118. // 为数组
  119. if (isArray) {
  120. val = pageParamVal;
  121. } else {
  122. val = pageParamVal[0];
  123. }
  124. } else if (aimTypeName.equals("int")) {
  125. if (isArray) {
  126. int[] array = new int[pageParamVal.length];
  127. for (int i = 0; i < pageParamVal.length; i++) {
  128. array[i] = Integer.parseInt(pageParamVal[i]);
  129. }
  130. val = array;
  131. } else {
  132. int v = Integer.parseInt(pageParamVal[0]);
  133. val = v;
  134. }
  135. } else if (aimTypeName.equals("byte")) {
  136. if (isArray) {
  137. byte[] array = new byte[pageParamVal.length];
  138. for (int i = 0; i < pageParamVal.length; i++) {
  139. array[i] = Byte.parseByte(pageParamVal[i]);
  140. }
  141. val = array;
  142. } else {
  143. byte v = Byte.parseByte(pageParamVal[0]);
  144. val = v;
  145. }
  146. } else if (aimTypeName.equals("boolean")) {
  147. if (isArray) {
  148. Boolean[] array = new Boolean[pageParamVal.length];
  149. for (int i = 0; i < pageParamVal.length; i++) {
  150. array[i] = Boolean.parseBoolean(pageParamVal[i]);
  151. }
  152. val = array;
  153. } else {
  154. Boolean v = Boolean.parseBoolean(pageParamVal[0]);
  155. val = v;
  156. }
  157. }else if (aimTypeName.equals("double")) {
  158. if (isArray) {
  159. Double[] array = new Double[pageParamVal.length];
  160. for (int i = 0; i < pageParamVal.length; i++) {
  161. array[i] = Double.parseDouble(pageParamVal[i]);
  162. }
  163. val = array;
  164. } else {
  165. Double v = Double.parseDouble(pageParamVal[0]);
  166. val = v;
  167. }
  168. }else if (aimTypeName.equals("char")) {
  169. if (isArray) {
  170. Character[] array = new Character[pageParamVal.length];
  171. for (int i = 0; i < pageParamVal.length; i++) {
  172. array[i] = pageParamVal[i].charAt(0);
  173. }
  174. val = array;
  175. } else {
  176. Character v = pageParamVal[0].charAt(0);
  177. val = v;
  178. }
  179. }
  180. return val;
  181. }
  182. }

2.5注解

annotation.zip