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 {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// 实际的处理在这, 由具体的子类FrameworkServlet实现
initServletBean();
}
复制代码
FrameworkServlet
的initServletBean
方法,真正核心的地方在于initWebApplicationContext()
protected final void initServletBean() throws ServletException {
long startTime = System.currentTimeMillis();
try {
// 初始化web容器上下文
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
// ...
}
复制代码
核心处理方法initWebApplicationContext
protected WebApplicationContext initWebApplicationContext() {
// ...省略部分代码
// 这个方法的主要逻辑是去调用onRefresh(ApplicationContext context)方法,onRefresh方法在DispatcherServlet类中被覆写,
// 以上面得到的上下文为依托,完成SpringMVC中默认实现类的初始化。
if (!this.refreshEventReceived) {
synchronized (this.onRefreshMonitor) {
// onRefresh方法在DispatcherServlet类中被覆写,所以从这里可以知道先创建spring容器, 在springBean都创建成功之后, 再通过onRefresh方法进行DispatcherServlet的初始化。
onRefresh(wac);
}
}
// 将这个上下文发布到ServletContext中,也就是将上下文以一个和Servlet类在web.xml中注册名字有关的值为键,设置为ServletContext的一个属性。你可以通过改变
// publishContext的值来决定是否发布到ServletContext中,默认为true。
if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
复制代码
通过onRefresh(wac)
方法,调用到子类DispatcherServlet
具体的初始化方法。
/**
* 初始化DispatcherServlet策略
*
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* 初始化DispatcherServlet策略(九大组件)
*
* Initialize the strategy objects that this servlet uses.
* May be overridden in subclasses in order to initialize further strategy objects.
*/
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
最终不会产生父子容器的原因。