源码剖析-DispatcherServlet初始化【子容器&9大组件】
1.DispatcherServlet类图
Web应用
启动的最后一个步骤就是创建和初始化相关Servlet
,我们配置了DispatcherServlet类
前端控制器,前端控制器作为中央控制器是整个Web应用
的核心,用于获取分发用户请求并返回响应。
其类图如下所示:
通过类图可以看出DispatcherServlet类
的间接父类实现了Servlet接口
,因此其本质上依旧是一个Servlet
2.HttpServletBean初始化
DispatcherServelt
类的本质是Servlet
,所以在Web应用
部署到容器后进行Servlet
初始化时会调用相关的init(ServletConfig)
方法,因此,DispatchServlet类
的初始化过程也由该方法开始:
(注意:DispatcherServelt
没有init方法,会走到父类HttpServletBean
的init方法)
/**
* DispatcherServlet 初始化入口
* Map config parameters onto bean properties of this servlet, and
* invoke subclass initialization.
* @throws ServletException if bean properties are invalid (or required
* properties are missing), or if subclass initialization fails.
*/
@Override
public final void init() throws ServletException {
// Set bean properties from init parameters.
/**
* 1.加载初始化参数,如:
* <servlet>
* <servlet-name>example</servlet-name>
* <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
* <init-param>
* <param-name>name</param-name>
* <param-value>jack</param-value>
* </init-param>
* <load-on-startup>1</load-on-startup>
* </servlet>
* 这里会解析init-param列表。
*/
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;
}
}
// Let subclasses do whatever initialization they like.
// 2.留给子类覆盖的模板方法
initServletBean();
}
该方法最主要的作用就是初始化init-param,如果我们没有配置任何init-param,那么该方法不会执行任何操作。从这里我们没有拿到有用的信息,但是在该方法结尾有initServletBean(),这是一个模板方法,可以由子类来实现,那么接下来我们就去看其子类FrameworkServlet中的initServletBean
3.FrameworkServlet初始化
继续查看 initServletBean()。父类 FrameworkServlet 覆盖了 HttpServletBean 中的 initServletBean 函数,如下:
protected void initServletBean() throws ServletException {
}
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
// 为当前servlet初始化web应用上下文
this.webApplicationContext = initWebApplicationContext();
// 空的模板方法
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
protected WebApplicationContext initWebApplicationContext() {
// 获取rootContext,该Context就是通过ContextLoaderListener创建的XmlWebApplicationContext
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// 如果当前webApplicationContext不为null,则为其设置父容器
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
// 未能通过构造函数注入,则尝试去ServletContext容器中查找有无WebApplicationContext