这一篇记录SpringMvc的初始化过程
Springmvc的入口在DispacherServlet,这个类是配置在web.xml中的,容器启动时就会加载
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup> //如果不配置默认会在servlet第一次被访问时初始化,导致第一请求耗时较长
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
DispatcherServlet的初始化
通过以上的配置,Tomcat容器启动时默认会执行Servlet的init方法,查看DispactherServlet发现并没有这个方法,其初始化方法是在父类及其祖父类中逐步实现的。先看一下DispatcherServlet的继承关系
从上面可以看到DispatcherServlet的继承关系,红色框内为spring 框架内容。
- 首先,容器启动,会默认执行servlet的init方法,在这里我理解为,tomcat的启动执行了这么两行代码
Servlet servlet = new DispatcherServlet
servlet.init()
这是一种java多态的体现,DispatcherServlet没有init方法,那么就会执行执行父类的,层层往上找。第一个抽象类GenericServlet实现了Servlet接口中的init方法,一个空实现和init(ServletConfig config),这里分两个方法是方便编程人员对init的操作。
- 接下来是spring框架对init方法的实现。HttpServletBean中实现了GenericServlet的init方法
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
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.
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
在这里将配置在web.xml中的init-param参数设置到bean中,然后执行initServletBean(),但是这个方法也是个空实现,具体实现在其子类FrameworkServlet中
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = initWebApplicationContext(); //初始化web上下文。
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
在这里开始执行初始化web应用上下文
web上下文创建完成后执行onRefresh方法,最后由DispatcherServlet实现了onRefresh方法,开始对Springmvc的九大组件进行初始化,到此所有初始化工作完成。
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* <p>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);
}
总结
web框架用了三个类分别执行了部分初始化工作
HttpServletBean:读取web,xml中的init-param参数设置到servlet中
FrameworkServlet:将spring容器和servlet进行关联。WebApplicationContext是Servlet的一个属性,然后将ContextLoaderListener获取的上下文作为父上下文。
DispatcherServlet:ch:初始化组件,包括HandlerMapping,HandlerAdapter等。