SpringMVC工作流程如图:
可以看到DispatcherServlet是一个核心类,充当分发器的角色,它的继承关系如下图:
可以看出DispatcherServlet继承自Framework,继承自HttpServletBean,继承自HttpServlet。
所以DispatcherServler是一个Servlet。可由Servlet容器调用执行。
用源码解释流程图:
1、用户的请求会被tomcat发送到DispatchServlet
请求是如何经过tomcat到达servlet的参考:https://blog.csdn.net/sumengnan/article/details/111504047
2、dispatchServlet会先去查询处理器映射器,并返回处理器执行链(里面包含拦截器和handler)
到达servlet后最终会执行到doDispatch方法
getHandler方法内容如下:根据一开始初始化获取的handlerMapping挨个去找合适的处理器映射器
挨个调用handlerMapping各实现类的getHandlerInternal方法,如图:
试着能否解析出处理器Handler,如果能则继续往下执行,封装HandlerExecutionChain执行链并返回
通过new HandlerExecutionChain创建执行链,在通过addInterceptor添加拦截器,如图:
handlerMapping有多种实现类,分别用来处理不同的映射配置方式
例如:
- BeanNameUrlHandlerMapping :xml中bean标签配置处理器方法。例如:<bean id="aController" name="/a.action" class="com.xxx.simpleController"/>
- SimpleUrlHandlerMapping:xml中bean配置的urlMap属性来实现请求URL到控制器(controller handler bean)的映射。<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"><property name="urlMap"><map><entry key="a.action" value-ref="aController"/></map></property></bean>
- RequestMappingHandlerMapping :用来处理@RequestMapping注解的处理器方法
你也可以自定义处理器映射器HandlerMapping,但是要注意Order顺序(实现Order接口或@Order注解)
3、dispatchServlet去查询对应的处理器适配器,由处理器适配器调用自定义的controller(有拦截器先执行拦截器before方法),并返回ModelAndView对象(里面包含了逻辑视图),再执行拦截器post方法
继续在doDispatch方法向下执行到getHandlerAdapater方法
getHandlerAdapter方法内容如下:根据一开始初始化获取的handlerAdapter挨个去找合适的处理器适配器
HandlerAdapter接口有多种实现类如下:分别用来调用不同的Handler
例如:
- HttpRequestHandlerAdapter 用来调用实现了HttpRequestHandler接口的controller
- SimpleControllerHandlerAdapter 用来调用实现了Controller接口的controller
- RequestMappingHandlerAdapter 用来调用@RequestMapping注解的controller
常用的就这三种,其他的有兴趣可以试试。
SpringMVC实现控制器controller的三种方式?
1、实现Controller接口(返回值为ModelAndView)
2、实现HttpRequestHandler接口(无返回值)
3、@Controller或@RestController注解
如果想自定义Adapter来调用controller,可以实现HandlerAdapter接口,但是要注意Order顺序(实现Order接口或@Order注解)
调用拦截器的时机如下图:
这里还有一个关键点,就是当使用注解@RequestMapping时,反射调用的参数是如何解析的?
参考:https://blog.csdn.net/sumengnan/article/details/113774179
4、dispatchServlet去查询对应的视图解析器,并返回View(真实的视图)
继续在doDispatch方法向下执行到processDispatchResult方法
继续往下走,如果handler返回了mv视图,则会进入render方法,进行渲染操作,否则跳过
如果ModelAndView对象中设置了逻辑视图viewName的名字,则去查找对应的视图解析器,并返回view对象
ViewResolver视图解析器接口的实现类如下:
View视图接口的实现类如下:
5、渲染View,之后执行拦截器After方法
继续往下走,调用view接口的render渲染方法
执行拦截器after方法
6、返回用户
MVC自定义配置
三种方法:
- 实现WebMvcConfigurer接口
- 继承WebMvcConfigurerAdapter类 (已过时)
- 继承WebMvcConfigrutionSupport类
当springboot环境时,最好不要继承WebMvcConfigrutionSupport类。因为springboot自动配置类WebMvcAutoConfiguration有@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)注解,springboot就不会帮我们自动配置了,我们还需要手动配置各个组件,如ResourceHandlers 否则会导致访问静态资源的时候出现404
WebMvcConfigurer接口各方法解释,参考:https://blog.csdn.net/sumengnan/article/details/109064789
1、2、实现WebMvcConfigurer接口和继承WebMvcConfigurerAdapter类,其实是个效果,只是第二个过时了而已。
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/nihao").setViewName("success");
}
//……其他方法
}
3、继承WebMvcConfigrutionSupport类
@Configuration
class MyMvcConfig extends WebMvcConfigurationSupport {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/nihao").setViewName("success");
}
//……其他方法
}
扩展:
当我们是springboot环境时,可以在application.protires中配置如下内容:
# SPRING MVC
spring.mvc.locale=en_UK
spring.mvc.date-format=yyyy-MM-dd
其实最终也是使用的继承WebMvcConfigrutionSupport类的方式注册的配置。
springboot把我们的配置封装进WebMvcProperties类中