DispatcherServlet浅析
介绍
DispatcherServlet
是一个Servlet
,在springmvc
中被称为前端控制器,根据请求的路径、类型等进行请求的分发。
分析
DispatcherServlet
类图
上图中的红色部分为Servlet
接口,在Springmvc
中实现并扩展了该Servlet
接口。在我最早学javaweb
开发时,并不是上来就使用各种MVC
框架,而是从编写简单的Servlet
开始,就是继承HttpServlet
后,重写它的service
方法。而Springmvc
的设计同理,它也继承了HttpServlet
并重写它的service
方法,同时配置该servlet
拦截所有的请求,从而实现了由DispatcherServlet
来分发所有的请求。
1. 初始化
因为DispatcherServlet
实际上是一个Servlet
,所以当配置它生效时它同时也就遵循Servlet
的生命周期。在Servlet
的生命周期有三个阶段:
init()
:初始化请求service()
:获取到请求后的业务处理以及跳转distory()
:请求处理完成之后的销毁
所以当tomcat
容器(servlet
容器)启动时,会触发servlet
的初始化方法。HttpServletBean
实现了该方法。
@Override
public final void init() throws ServletException {
// ...
// 实际的处理在这, 由具体的子类FrameworkServlet实现
initServletBean();
}
FrameworkServlet
的initServletBean
方法,真正核心的地方在于initWebApplicationContext()
protected final void initServletBean() throws ServletException {
long startTime = System.currentTimeMillis();
try {
// 初始化web容器上下文
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
// ...
}
核心处理方法initWebApplicationContext
- 这个方法的主要逻辑是去调用
onRefresh
方法,onRefresh
方法在DispatcherServlet
类中被覆写,以上面得到的上下文为依托,完成SpringMVC
中默认实现类的初始化。 onRefresh
方法在DispatcherServlet
类中被覆写,所以从这里可以知道先创建spring
容器, 在springBean
都创建成功之后, 再通过onRefresh
方法进行DispatcherServlet
的初始化。- 将这个上下文发布到
ServletContext
中,也就是将上下文以一个和Servlet
类在web.xml
中注册名字有关的值为键,设置为ServletContext
的一个属性。你可以通过改变publishContext
的值来决定是否发布到ServletContext
中,默认为true
。
protected WebApplicationContext initWebApplicationContext() {
// ...省略部分代码
// 1.
if (!this.refreshEventReceived) {
synchronized (this.onRefreshMonitor) {
// 2.
onRefresh(wac);
}
}
// 3.
if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
通过onRefresh(wac)
方法,调用到子类DispatcherServlet
具体的初始化方法。
初始化DispatcherServlet
策略
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
初始化DispatcherServlet
策略,分别初始化九大组件(上传组件处理器, 处理器映射器, 理器适配器, 处理器异常整理器, 视图处理器等)
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
2. 分发逻辑
- 当客户端发起请求,请求进入
DispatcherServlet
,由它来进行处理器Handler
的查找。 - 首先根据请求信息
HttpServletRequest
,遍历已注册的所有handlerMappings
。返回的结构为HandlerExecutionChain
,是包含了具体的处理器handler
和拦截器interceptor
的结构。 - 获取到
HandlerExcecutionChain
后,根据具体的处理器,遍历所有的handlerAdapters
,返回支持的HandlerAdapter
。 - 在获取到
HandlerAdapter
之后,执行具体的handler
之前,会先遍历执行HandlerExecutionChain
中的拦截器的前置拦截方法preHandle()
,若出现某个拦截器的前置方法执行后方法false
,则直接从当前节点开始往前执行afterCompletion()
,执行完毕后直接终止请求。 - 根据
HandlerAdapter
处理器适配器执行具体的处理器Handler
逻辑。 - 在执行完成具体的
handler
之后,会遍历执行HandlerExecutionChain
中拦截器的postHandle
方法。 Handler
执行完毕之后会返回ModeAndView
。- 执行正常的情况下,在渲染模板后,请求返回前,会遍历执行
HandlerExecutionChain
中拦截器的afterCompletion
方法。
3.设置springmvc容器
根据类图可知DispatcherServlet
也是实现了ApplicationContextAware
接口,而xxxAware
接口,是在springbean
初始化时的一个postProcessBeforeInitialization
扩展点,由类ApplicationContextAwareProcessor
去执行具体的逻辑,简单来说就是将ApplicationContext
通过setApplicationContext
的方法传递给正在初始化的这个bean
。
在springboot
中就是通过这个方法,将spring
容器(applicationContext
)传给DispatcherServlet
,待到真正去执行DispatcherServlet
的初始化方法时this.applicationContext
就不为空,所以在initWebApplicationContext()
方法中,就走第一个if
的逻辑,这也是springboot
最终不会产生父子容器的原因。
转载自:https://juejin.cn/post/7061160723988086797