SpringMvc源码分析-spring容器初始化

1.前言

从web.xml解析SpringMvc容器初始化过程,基于Spring4.0+版本

2.web.xml

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
    /* ContextLoaderListener监听器,当tomcat启动的时候,调用其初始化方法*/
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    /* 当tomcat启动的时候,初始化此servlet*/
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

3.ContextLoaderListener(Tomcat启动时会创建)

 1、tomcat初始化调用contextInitialized方法(是父类ContextLoader方法

@Override
public void contextInitialized(ServletContextEvent event) {
   /*传入tomcat中servlet容器,即tomcat的上下文环境*/
   initWebApplicationContext(event.getServletContext());
}

2、initWebApplicationContext方法(去除了部分日志代码)

这里初始了spring容器

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
   /*这里后面WebApplicationContext会放到ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE这个key对应的value中*/
   /*此时servlet容器中已经有WebApplicationContext容器那么报错*/
   if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
      throw new IllegalStateException(
            "Cannot initialize context because there is already a root application context present - " +
            "check whether you have multiple ContextLoader* definitions in your web.xml!");
   }

   long startTime = System.currentTimeMillis();
   try {

      /*这个时候肯定为空*/
      if (this.context == null) {
         /*创建WebApplicationContext*/
         this.context = createWebApplicationContext(servletContext);
      }
      /*if没什么意义,百分百是ConfigurableWebApplicationContext,不太理解*/
      if (this.context instanceof ConfigurableWebApplicationContext) {
         ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
         /*判断是否刷新过,默认肯定没刷新*/
         if (!cwac.isActive()) {
             /*默认为空*/
            if (cwac.getParent() == null) {
                
               ApplicationContext parent = loadParentContext(servletContext);
               cwac.setParent(parent);
            }
            /*刷新spring容器*/
            configureAndRefreshWebApplicationContext(cwac, servletContext);
         }
      }
      /*将spring容器放入servlet容器中*/
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
      
      /*使用线程上下文加载,这里设计很棒,如果spring,jar在WEB-inf下那么由WebAppClassLoader 加载*/
      /*这里不做分析,读者有兴趣自己可以了解TCCL打破双亲委派原则*/
      ClassLoader ccl = Thread.currentThread().getContextClassLoader();
      if (ccl == ContextLoader.class.getClassLoader()) {
         currentContext = this.context;
      }
      else if (ccl != null) {
         currentContextPerThread.put(ccl, this.context);
      }
      return this.context;
   }
}

3.调用createWebApplicationContext方法

返回ConfigurableWebApplicationContext对象,本质是XmlWebApplicationContext

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
   /*获取到WebApplicationContext实例*/
   Class<?> contextClass = determineContextClass(sc);
   if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
      throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
            "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
   }
   /*XmlWebApplicationContext父类实现了ConfigurableWebApplicationContext接口所以强转*/
   return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

4.调用determineContextClass方法

   如果没有自定义就生成WebApplicationContext实例,加载XmlWebApplicationContext类(其实就是spring容器上下文它解析了默认的配置文件,后期在spring源码解析中分析)

protected Class<?> determineContextClass(ServletContext servletContext) {
   /*获取web.xml文件中 key为contextClass 的参数,就是可以自定义容器类*/
   String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
   if (contextClassName != null) {
      try {
         return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
      }
      catch (ClassNotFoundException ex) {
         throw new ApplicationContextException(
               "Failed to load custom context class [" + contextClassName + "]", ex);
      }
   }
   else {
      /*通过反射实例化webApplicationContext类*/
      /*这里defaultStrategies是ContextLoaderListener静态代码块中初始化,会加载一个配置文件*/
      /*加载了XmlWebApplicationContext*/
      contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
      try {
         return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
      }
      catch (ClassNotFoundException ex) {
         throw new ApplicationContextException(
               "Failed to load default context class [" + contextClassName + "]", ex);
      }
   }
}

4.总结

ContextLoaderListener的作用:初始化spring容器,将配置文件中的bean类都刷新实例化(具体实例化过程以后分析)

猜你喜欢

转载自blog.csdn.net/qq_38052979/article/details/81183252