这里写自定义目录标题
- spring mvc执行流程概括
- 执行流程图
- 如何根据用户请求url找到控制器对应的方法
- HandleMapping(处理器映射器)
- handleAdapter(处理器适配器)
- ViewResolver(视图解析器)
- 扩展点
spring mvc执行流程概括
spring mvc执行的流程可以简单概括为:第一步是找控制器,第二步是执行控制器,然后返回视图对象,把视图对象交给视图解析器,去渲染,最后响应给用户。
执行流程图
(1)第一步是找控制器,通过HandleMapping(处理器映射器),遍历所有实现了HandleMapping接口的实现类对象,找到合适的handle,返回给中央控制器(DispatcherServlet),最终返回的是一个handleExecutionChain类对象
(2)第二步是执行控制器,第一步已经拿到了控制器,接下来要去执行控制器,也就是拿到控制器适配器(handleAdapter中执行控制器,执行handler 方法,得到 ModelAndView 对象,为什么需要适配器,可以理解为我们现在都是使用@Controller、@RequestMapping注解来标注控制器,其实还可以通过继承Controller类实现handleRequest方法来处理用户请求,请求的地址还可以在xml中配置,总之就是执行控制器的方法不一样,所以spring mvc设计了不同的适配器来执行
(3)返回视图对象,然后把视图对象交给视图解析器,去渲染,最后响应给用户。
如何根据用户请求url找到控制器对应的方法
如果我们自己实现一个mvc框架,要实现这个功能可能要做以下几个步骤:
1、扫描整个项目所有的类
2、判断类上面有没有加@Controller、@Requestmapping注解
3、扫描方法、判断有没有注解
4、把注解的值和方法绑定起来,放到一个map里面HashMap<String,Method> map
5、自已定义一个MyDispatcherServlet,拦截用户请求,获取请求的url,根据url从map对象里面获取方法对象,通过反射执行方法里面的业务逻辑
通过源码分析一下spring mvc内部的工作流程:
1、首先看下最核心的一个类DispatcherServlet的继承关系,它既然是个Servlet,必然有init()、service()、doGet()和doPost()方法,在doGet()、doGet()方法里面会调用processRequest(request, response)——>doService(request, response)—>doDispatch(request, response),最核心的逻辑都在doDispatch方法里面完成
在doGet()、doGet()方法里面会调用processRequest(request, response)
在processRequest方法里面调用doService(request, response)
最后找到最核心的方法doDispatch
下面重点分析一下doDispatch方法里面的逻辑
经过上面的分析,大概了解了spring mvc接收请求的处理和调用过程,关于HandleMapping初始化以及各种handlemapping的作用后续再分析
HandleMapping(处理器映射器)
HandleMapping接口负责根据request请求找到对应的handle处理器及Intercepter拦截器,并将它们封装在handleExecutionChain对象中,返回给中央处理器,常用的实现类主要有:
(1)RequestMappingHandlerMapping
(2)SimpleUrlHandlerMapping
(3)BeanNameUrlHandlerMapping
handleAdapter(处理器适配器)
1、 Spring MVC 通过HandlerAdapter来实际调用处理函数,就是调用具体的方法对用户发来的请求来进行处理,当handlerMapping获取到执行请求的controller时,DispatcherServlte会根据对应的controller类型来调用相应的HandlerAdapter来进行处理。
2、DispatcherServlte会根据配置文件信息注册HandlerAdapter,DispatcherServlte会读取DispatcherServlte.properties文件,该文件中配置了三种HandlerAdapter:HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter和AnnotationMethodHandlerAdapter,比如:我们的控制器如果实现了Controller接口,那么就要实现Controller接口的handleRequest方法,实现了Controller接口的控制器会由SimpleControllerHandlerAdapter适配器开执行,SimpleControllerHandlerAdapter的实现也是很简单的,就是适配执行Controller的handleRequest方法。
**Controller接口**的定义也很简单,仅仅定义了一个handleRequest方法:
public interface Controller {
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
**SimpleControllerHandlerAdapter**类实现了HandlerAdapter 接口,其中有两个方法supports方法就是判断handler是否是Controller接口的实现类,handle方法本质是执行Controller.handleRequest方法
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
//判断是否是Controller类
return (handler instanceof Controller);
}
//执行Controller的handleRequest方法
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//本质是执行Controller的handleRequest方法
return ((Controller) handler).handleRequest(request, response);
}
}
ViewResolver(视图解析器)
扩展点
1、自定义参数解析器
我们经常用的,比如用注解 @RequestBody 修饰参数,最终 SpringMVC 会通过自带的 RequestResponseBodyMethodProcessor 解析器进行解析,使用 Jackson 提供的 MappingJackson2HttpMessageConverter 转换器将JSON数据转换成我们想要的格式。如果提交的是正常表单数据,用 @RequestParam 修饰参数,最终 SpringMVC 会通过自带的 RequestParamMethodArgumentResolver 解析器解析出表单里面的 value,然后找到合适的转换器将数据装换成我们想要的格式。那么如何自定义一个参数解析器那?
自定义解析器需要实现HandlerMethodArgumentResolver接口,HandlerMethodArgumentResolver接口包含两个方法:
supportsParameter:用于判定是否需要处理该参数分解,返回true为需要,并会去调用下面的方法
resolveArgument:真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象。
通过定义个配置类继承WebMvcConfigurationSupport或者WebMvcConfigure类在addArgumentResolvers方法里面把自定义的参数解析器添加进去,这些spring mvc在进行参数解析的时候会调用
public class UserArgumentsResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(UserParam .class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
UserInfo userVo = new UserInfo ();
userVo.setName("张三");
userVo.setDept("研发部")
return userVo;
}
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserParam {}
@ResponseBody
@RequestMapping(value = “test”, method = RequestMethod.GET)
public String test(@UserParam UserInfo userInfo) {
System.out.println(userInfo.getName());
return “success”;
}