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

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

  
    
        org.springframework.web.context.ContextLoaderListener
    
  

 加上这个配置后Tomcat在启动的时候会去读取web.xml文件中的两个结点,接着,会创建一个ServletContext(servlet上下文),这个web项目的所有部分都将共享这个上下文,然后将转换为键值对,并交给servletContext。 还会创建中的类实例,创建监听器。此时就会创建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启动的时候启动的)