tomcat启动创建spring父子容器
一、小知识课堂
1.关键性接口ServletContextListener
监听器ServletContextListener
,监听ServletContext的生命周期。换句话说,监听Web容器的生命周期。所以说,如果tomcat启动时就加载spring容器,必然与该监听器有关联。
public interface ServletContextListener extends EventListener {
// tomcat启动时,触发该方法
void contextInitialized(ServletContextEvent var1);
// tomcat销毁时,触发该方法
void contextDestroyed(ServletContextEvent var1);
}
2.tomcat加载web.xml流程
tomcat启动时会加载web.xml,加载顺序为context-param ⇒ listener ⇒ filter ⇒ servlet
。
- 第一步,将
context-param
属性设置到ServletContext中。后面如果使用,可以通过ServletContext中的getInitParameter()方法获取指定的属性值。ServletContext sc; String configLocationParam = sc.getInitParameter("param-name");
- 第二步,将listener加载到applicationListeners中,对于
ServletContextListener
,会调用listener.contextInitialized(event)。 - 第三步,加载filter元素,然后实例化filter设置到filterConfigs里。
- 第四步:tomcat加载servlet到context,此处检查是否存在load-on-startup,若存在立刻load。
3.servlet生命周期
针对于创建springMVC容器使用DispatchServlet,为了更好的理解springmvc容器引入的小知识点。
第一阶段:默认情况下,当servlet第一次接收到请求时,会创建实例。以后每一次请求该servlet,是同一个servlet,不会创建新的;如果servlet加了
load-on-startup
,则表示容器创建时servlet就创建实例。
第二阶段:实例化之后,容器会调用init()方法且只会调用一次
。这是在处理请求前完成的一些初始化工作,可以从ServletConfig中获取初始化参数信息。
第三阶段:每当发送一个请求时,Servlet容器调用相应的Servlet中service()方法对请求进行处理
,需要注意的是,在service()方法调用之前,init()方法是必须成功执行的。
第四阶段:servlet容器检测到Servlet实例该从服务中被移除时,容器会调用实例的destroy()
方法,让该实例可以释放它所使用的资源,从而保存数据到持久存储设备中。
根据上面所介绍的知识点,在tomcat中加载spring容器,只需要在web.xml中配置如下:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
二、创建Spring容器
- ContextLoaderListener 实现了
ServletContextListener
,所以当tomcat容器启动时,会触发contextInitialized方法,进而调用initWebApplicationContext
方法。public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public void contextInitialized(ServletContextEvent event) { this.initWebApplicationContext(event.getServletContext()); } // 其它方法... }
- 创建容器并放置到servletContext中。
配置并刷新容器 的关键性代码:
configLocationParam = sc.getInitParameter("contextConfigLocation");
wac.refresh();
private WebApplicationContext context; // 1. 创建spring容器 if (this.context == null) { this.context = this.createWebApplicationContext(servletContext); } // 2. 配置并刷新容器 if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context; if (!cwac.isActive()) { if (cwac.getParent() == null) { ApplicationContext parent = this.loadParentContext(servletContext); cwac.setParent(parent); } this.configureAndRefreshWebApplicationContext(cwac, servletContext); } } // 3. 将容器存入servletContext中,key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
- 获取spring容器
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext())
三、创建SpringMVC容器
- 如果要使用SpringMVC容器,需要在web.xml中配置如下。
<servlet> <servlet-name>DispatcherServlet</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-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
- tomcat启动会加载servlet,因为配置了
load-on-startup
,并且根据servlet生命周期可以推断DispatcherServlet会在容器启动之后立即创建实例
并初始化(init()方法)
。阅读DispatcherServlet源码,发现没有init()方法,发现init()方法在父类HttpServletBean被重写中。// 创建DispatcherServlet实例 public DispatcherServlet() { this.setDispatchOptionsRequest(true); } // DispatcherServlet初始化 public final void init() throws ServletException { this.initServletBean(); } // protected final void initServletBean() throws ServletException { this.webApplicationContext = this.initWebApplicationContext(); } protected WebApplicationContext initWebApplicationContext() { // 获取父容器,此处获得的为上面所创建的spring容器 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // xxx逻辑 } if (wac == null) { // 创建容器 wac = this.createWebApplicationContext(rootContext); } return wac; } protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { // 此处获得 XmlWebApplicationContext.class Class<?> contextClass = this.getContextClass(); // 反射得到 XmlWebApplicationContext ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); wac.setEnvironment(this.getEnvironment()); // 设置父容器为spring容器 wac.setParent(parent); // classpath:springmvc.xml String configLocation = this.getContextConfigLocation(); if (configLocation != null) { // 设置配置文件路径 wac.setConfigLocation(configLocation); } // 配置刷新容器 this.configureAndRefreshWebApplicationContext(wac); return wac; }