美文网首页Spring核心内容
Spring如何在Tomcat启动的时候启动的

Spring如何在Tomcat启动的时候启动的

作者: szhlcy | 来源:发表于2019-04-12 14:08 被阅读0次

     在我们使用spring跟tomcat进行结合的时候,我们都会在Resources文件下创建一个webapp/WEB-INF文件夹下面创建一个web.xml文件,在这个文件中我们会添加这么几行配置

      <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
      </listener>
    

     加上这个配置后Tomcat在启动的时候会去读取web.xml文件中的<listener>和<context-param>两个结点,接着,会创建一个ServletContext(servlet上下文),这个web项目的所有部分都将共享这个上下文,然后将<context-param>转换为键值对,并交给servletContext。 还会创建<listener>中的类实例,创建监听器。此时就会创建ContextLoaderListener类,在初始化Web应用程序中的任何过滤器或servlet之前,将调用类中的contextInitialized方法。

        public void contextInitialized(ServletContextEvent event) {
            initWebApplicationContext(event.getServletContext());
        }
    

     这个方法会为给定的servlet上下文初始化Spring的Web应用程序上下文,进入到里面调用的initWebApplicationContext方法,这个方法定义在ContextLoaderListener的父类ContextLoader中

    public class ContextLoader {
        public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
            ...
            try {
                //如果WebApplicationContext还是null,一般使用web.xml的形式配置的时候,值会为null,这时候使用的是ContextLoaderListener的默认的空构造器
                if (this.context == null) {
                    //创建WebApplicationContext
                    this.context = createWebApplicationContext(servletContext);
                }
                //如果WebApplicationContext是ConfigurableWebApplicationContext类型的时候会进行一些处理
                if (this.context instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                    //如果此时的上下文还没有激活,第一次进来的时候是没有激活的即false,当上下文刷新之后,这个值回时true
                    if (!cwac.isActive()) {
                        // The context has not yet been refreshed -> provide services such as  setting the parent context, setting the application context id, etc
                        //如果父上下文还没有设置,就将servletContext设置为父上下文,因为此时很多信息都是里面后面会用到
                        if (cwac.getParent() == null) {
                            // The context instance was injected without an explicit parent ->  determine parent for root web application context, if any.
                            ApplicationContext parent = loadParentContext(servletContext);
                            cwac.setParent(parent);
                        }
                        //配置并刷新上下文,会把servletContext中的一些属性放到spring的上下文中,例如如果我们配置了的servlet的initParam参数会被获取
                        configureAndRefreshWebApplicationContext(cwac, servletContext);
                    }
                }
                //将spring的上下文保存到servletContext中
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
                ClassLoader ccl = Thread.currentThread().getContextClassLoader();
                if (ccl == ContextLoader.class.getClassLoader()) {
                    currentContext = this.context;
                }
                //将上下文存储在本地实例变量中,以保证它在ServletContext关闭时可用
                else if (ccl != null) {
                    currentContextPerThread.put(ccl, this.context);
                }
                if (logger.isInfoEnabled()) {
                    long elapsedTime = System.currentTimeMillis() - startTime;
                    logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
                }
    
                return this.context;
                }
            ...
        }
    }
    

     当我们使用xml的形式配置listener的时候默认WebApplicationContext为null,此时就会自动创建一个WebApplicationContext,所以进入到createWebApplicationContext方法

        protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        //根据ServletContext来获取WebApplicationContext的Class类型
            Class<?> contextClass = determineContextClass(sc);
            if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
                throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                        "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
            }
            //实例化WebApplicationContext
            return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
        }
    

     接下来进入determineContextClass方法

        protected Class<?> determineContextClass(ServletContext servletContext) {
            //如果有实现WebApplicationContext并设置了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 {
                //从defaultStrategies中获取默认的WebApplicationContext的ClassName
                contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
                try {
                    //根据contextClassName获取Class对象
                    return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
                }
                catch (ClassNotFoundException ex) {
                    throw new ApplicationContextException(
                            "Failed to load default context class [" + contextClassName + "]", ex);
                }
            }
        }   
    

     一般情况下我们都不会去设置ServletContext类的contextClass的配置,所以这里会使用默认值,进入到defaultStrategies这个对象,发现这个对象在ContextLoader的静态代码块中被初始化

            private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
        static {
            try {
                ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
                defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
            }
            catch (IOException ex) {
                throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
            }
        }
    

     可以看到defaultStrategies的值是从配置文件ContextLoader.properties中获取的,所以可以直接打开文件查看

    org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
    

     到这里就知道默认实例化的WebApplicationContext是XmlWebApplicationContext。既然上下文实例已经创建了,接下来就是刷新了。这里就要进入到initWebApplicationContext方法中的调用的configureAndRefreshWebApplicationContext方法了,主要看其中的refresh方法。

        protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        ...
        //前面做的是为上下文刷新之前的做的准备,比如设置ServletContext到spring的上下文中,获取
        //刷新上下文
        wac.refresh();
        ...
        }
    

     这里的就是开启Spring的容器的刷新了,也是最核心的了。这里调用的是AbstractApplicationContext类的refresh,关于这个前面已经讲解过5.2Spring源码解析——refresh方法,注意在后面调用loadBeanDefinitions方法的时候调用的是XmlWebApplicationContext中的loadBeanDefinitions。

    相关文章

      网友评论

        本文标题:Spring如何在Tomcat启动的时候启动的

        本文链接:https://www.haomeiwen.com/subject/vsrcwqtx.html