1.SpringMVC的工作流程

- 客户发送请求 (request)
- 请求交由核心处理(DispatcherServlet)
- 核心控制器找到映射器,映射器看看请求路径是什么(HandleMapping)
- 控制器再找到适配器,看看有哪些类实现了Controller接口或者对应的Bean对象(HandleAdapter)
- 将带过来的数据进行转换,格式化等等操作 找到我们的控制器Action,处理完业务之后返回
- 最后通过视图解析器来对ModelAndView进行解析
- 跳转到对应的JSP/html页面
2.核心代码
2.1 在web.xml中配置使得controller的请求都经过DispatcherServlet
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
private void doScanner(String packageName) {// 类全名:包名+类名// 将包名转化为文件路径String packagePath = packageName.replaceAll("\\.", "/");// 获取src文件下的真实路径,widow中空格是%20,要将他转换成空格String path = DispatcherServlet.class.getClassLoader().getResource(packagePath).getPath().replace("%20", " ");// 创建file对象File file = new File(path);// 判断文件是否存在if (!file.exists()) {return;}// 获取子文件File[] files = file.listFiles();for (File f : files) {if (f.isDirectory()) {doScanner(packageName + "." + f.getName());} else {// 获取源文件名称String fileName = f.getName();// 获取类名String name = fileName.replace(".class", "");// 类全名String className = packageName + "." + name;// 存储到controllers中//System.out.println(className);classNames.add(className);}}System.out.println(path);}private void loadProperties(ServletConfig config) {// 根据name获取servlet初始化参数值String configName = config.getInitParameter("config");// 如果用户配置了文件名,则以配置的为准,否则取默认值if (configName == null) {configName = "application.properties";}InputStream is = null;try {is = DispatcherServlet.class.getClassLoader().getResourceAsStream(configName);configProp.load(is);} catch (Exception e) {e.printStackTrace();} finally {try {if (is != null)is.close();} catch (IOException e) {e.printStackTrace();}}}protected void service(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {Object result = handlerAdapter.adapter(request, response);if (result==null) {return;}//获取当前方法对象Method method=handlerMapper.getMethodMapper().get(request.getPathInfo());if (method.isAnnotationPresent(ResponseBody.class)) {//设置响应内容类型response.setContentType("application/json;charset=utf-8");//将返回值的java对象转化成json,并通过指定输出流写出new ObjectMapper().writeValue(response.getOutputStream(),result);}else if(method.isAnnotationPresent(ResponseForward.class)){String path=(String)result;//请求转发request.getRequestDispatcher("../../"+path).forward(request, response);}else if(method.isAnnotationPresent(ResponseRedirect.class)){String path=(String)result;//重定向response.sendRedirect(path);}}
}
<a name="6zImK"></a>## 2.3HandlerMapping```javapackage com.woniuxy.myspringmvc.core;import java.lang.reflect.Method;import java.util.HashMap;import java.util.List;import java.util.Map;import com.woniuxy.myspringmvc.annotation.Controller;import com.woniuxy.myspringmvc.annotation.RequestMapping;public class HandlerMapper {// 存储controller的实例,键:类名,值:对应controller对象private Map<String, Object> controllers = new HashMap<String, Object>();// 存储请求URL与controller映射,键:请求URL,值controller对象private Map<String, Object> controllerMapper = new HashMap<String, Object>();// 存储请求URL与controller映射,键:请求URL,值controller对象的方法private Map<String, Method> methodMapper = new HashMap<String, Method>();public HandlerMapper(List<String> classNames) {// 实例化:创建添加了controller注解的controller对象,并存储到controllers中doInstance(classNames);// 建立映射,建立请求与controller的方法映射initHandlerMapper();}private void initHandlerMapper() {// 循环遍历每个controller对象for (Object controller : controllers.values()) {// 获取当前controller对象的class实例;Class cl = controller.getClass();// 判断当前controller类上是否存在RequestMapping注解if (cl.isAnnotationPresent(RequestMapping.class)) {// 获取requestmapping注解RequestMapping controllerRequestMapping = (RequestMapping) cl.getDeclaredAnnotation(RequestMapping.class);// 获取注解值String controllerRequestMappingValue = controllerRequestMapping.value();// 获取controller类所有方法Method[] methods = cl.getDeclaredMethods();for (Method method : methods) {// 判断当前方法是否存在requestmapping注解if (method.isAnnotationPresent(RequestMapping.class)) {// 获取当前方法的requestmapping注解RequestMapping methodRequestMapping = method.getDeclaredAnnotation(RequestMapping.class);// 获取当前方法上的request1mapping注解值String methodRequestMappingValue = methodRequestMapping.value();// 生成键String key = controllerRequestMappingValue + methodRequestMappingValue;// 建立请求与controller的映射controllerMapper.put(key, controller);// 建立请求与method的映射methodMapper.put(key, method);}}}}}private void doInstance(List<String> classNames) {// 循环遍历每个controller全类名for (String className : classNames) {try {// 通过类全名获取class实例Class cl = Class.forName(className);// 判断当前controller上是否存在controlle注解if (cl.isAnnotationPresent(Controller.class)) {// 创建目标controller对象Object o = cl.newInstance();// 存储到controllers中controllers.put(cl.getSimpleName(), o);}} catch (Exception e) {e.printStackTrace();}}}public Map<String, Object> getControllers() {return controllers;}public void setControllers(Map<String, Object> controllers) {this.controllers = controllers;}public Map<String, Object> getControllerMapper() {return controllerMapper;}public void setControllerMapper(Map<String, Object> controllerMapper) {this.controllerMapper = controllerMapper;}public Map<String, Method> getMethodMapper() {return methodMapper;}public void setMethodMapper(Map<String, Method> methodMapper) {this.methodMapper = methodMapper;}}
2.4HandlerAdapter
package com.woniuxy.myspringmvc.core;import java.io.IOException;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Parameter;import java.util.Iterator;import java.util.Map;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class HandlerAdapter {private HandlerMapper handlerMapper;public HandlerAdapter(HandlerMapper handlerMapper) {this.handlerMapper = handlerMapper;}public Object adapter(HttpServletRequest request, HttpServletResponse response) throws IOException {Object result = null;// 获取请求的映射键String key = request.getPathInfo();// 根据键查找对应controller对象以及method对象Object controller = handlerMapper.getControllerMapper().get(key);Method method = handlerMapper.getMethodMapper().get(key);if (controller == null | method == null) {response.sendError(404, "请求的controller或method不存在!");return result;}// 获取参数个数int methodParamCount = method.getParameterCount();// 存储方法参数实参Object[] realMethodParams = new Object[methodParamCount];// 获取页面所有参数的map对象Map<String, String[]> pageParamVals = request.getParameterMap();// 获取方法所有参数Parameter[] methodParames = method.getParameters();for (int i = 0; i < methodParames.length; i++) {// 每一个形参Parameter methodParam = methodParames[i];// 获取方法参数类型Class methodParamTypeCl = methodParam.getType();// 获取参数方法名称String methodTypeName = methodParamTypeCl.getSimpleName();// 获取方法参数名称String methodParamName = methodParam.getName();// 判断参数数据类型if (methodTypeName.equals("HttpServletRequest")) {realMethodParams[i] = request;} else if (methodTypeName.equals("HttpServletResponse")) {realMethodParams[i] = response;} else if (methodParamTypeCl.getClassLoader() == null) {// java自带类型,比如String、int等// 取出当前方法的参数的实参String[] pageParamVal = pageParamVals.get(methodParamName);// 判断参数是否存在if (pageParamVal == null) {response.sendError(404, method.getName() + "方法的" + methodParamName + "参数不存在!");return result;}// 将客户端传参进行类型转换realMethodParams[i] = parseType(pageParamVal, methodParamTypeCl);} else {// 自定义类型Object o = null;try {// 创建目标类对象o = methodParamTypeCl.newInstance();// 获取当前类的属性Field[] files = methodParamTypeCl.getDeclaredFields();for (Field field : files) {// 获取当前属性名称String fieldName = field.getName();// 根据属性名和方法参数名称获取前端参数键String pageParamKey = methodParamName + "." + fieldName;// 尝试从前端参数中取值String[] pageParamVal = pageParamVals.get(pageParamKey);if (pageParamVal == null) {continue;}// 获取当前属性类型Class fieldTypeCl = field.getType();// 将String转为真实类型Object val = parseType(pageParamVal, fieldTypeCl);// 赋值// 获取set方法名称String fieldSetMethodName = "set" + (fieldName.charAt(0) + "").toUpperCase()+ fieldName.substring(1);// 获取set方法对象Method fieldSetMethod = methodParamTypeCl.getDeclaredMethod(fieldSetMethodName, fieldTypeCl);// 执行set方法,赋值fieldSetMethod.invoke(o, val);}} catch (Exception e) {e.printStackTrace();}realMethodParams[i] = o;}}try {result = method.invoke(controller, realMethodParams);} catch (Exception e) {e.printStackTrace();}return result;}private Object parseType(String[] pageParamVal, Class methodTypeCl) {Object val = null;// 存储当前目标类型是否为数组类型boolean isArray = false;// 存储元素类型名称String aimTypeName = null;// 判断目标类型是否为数组if (methodTypeCl.isArray()) {isArray = true;aimTypeName = methodTypeCl.getComponentType().getSimpleName();} else {aimTypeName = methodTypeCl.getSimpleName();}// 分别判断目标类型,进行类型转换if (aimTypeName.equals("String")) {// 为数组if (isArray) {val = pageParamVal;} else {val = pageParamVal[0];}} else if (aimTypeName.equals("int")) {if (isArray) {int[] array = new int[pageParamVal.length];for (int i = 0; i < pageParamVal.length; i++) {array[i] = Integer.parseInt(pageParamVal[i]);}val = array;} else {int v = Integer.parseInt(pageParamVal[0]);val = v;}} else if (aimTypeName.equals("byte")) {if (isArray) {byte[] array = new byte[pageParamVal.length];for (int i = 0; i < pageParamVal.length; i++) {array[i] = Byte.parseByte(pageParamVal[i]);}val = array;} else {byte v = Byte.parseByte(pageParamVal[0]);val = v;}} else if (aimTypeName.equals("boolean")) {if (isArray) {Boolean[] array = new Boolean[pageParamVal.length];for (int i = 0; i < pageParamVal.length; i++) {array[i] = Boolean.parseBoolean(pageParamVal[i]);}val = array;} else {Boolean v = Boolean.parseBoolean(pageParamVal[0]);val = v;}}else if (aimTypeName.equals("double")) {if (isArray) {Double[] array = new Double[pageParamVal.length];for (int i = 0; i < pageParamVal.length; i++) {array[i] = Double.parseDouble(pageParamVal[i]);}val = array;} else {Double v = Double.parseDouble(pageParamVal[0]);val = v;}}else if (aimTypeName.equals("char")) {if (isArray) {Character[] array = new Character[pageParamVal.length];for (int i = 0; i < pageParamVal.length; i++) {array[i] = pageParamVal[i].charAt(0);}val = array;} else {Character v = pageParamVal[0].charAt(0);val = v;}}return val;}}
