必威体育Betway必威体育官网
当前位置:首页 > IT技术

Spring的ContextLoaderListener加载上下文的源码分析

时间:2019-09-28 23:45:42来源:IT技术作者:seo实验室小编阅读:88次「手机版」
 

contextloaderlistener

前言:

1,如果使用自定义的监听器,需要经过下面的步骤 1到步骤10

2,如果使用Spring自己的监听器contextloaderlistener,需要经过下面的步骤6到步骤10

3,web.xml中的加载顺序:context-param -> listener -> filter -> servlet

上下文加载步骤分析:

1,在WebService的 web.xml可以定义一个监听器:WebContextLoaderListener:

<listener>
    <listener-class>com.dangdang.ddframe.web.WebContextLoaderListener</listener-class>
</listener>

WebContextLoaderListener类:

package com.dangdang.ddframe.web;

public final class WebContextLoaderListener extends ContextLoaderListener {

   
    private static ContainerInitializer containerInitializer = ContainerInitializer.getinstance();

   

    @Override

    public void contextInitialized(final ServletContextEvent event) {

        containerInitializer.startContainer(new AbstractInitializeCallbackAdapter() {

           

            @Override

            public APPlicationContext doInitialize() {

                ServletContext servletContext = event.getServletContext();

                servletContext.setInitparameter(ContextLoader.CONFIG_LOCATION_PARAM, SpringContainer.CONFIG_FILE);

                WebContextLoaderListener.super.contextInitialized(event);

                return webapplicationContextUtils.getWebApplicationContext(servletContext);

            }

        });

    }

}

ContainerInitializer在初始化时,初始化了参数加载器,读取了日志级别。

2,因为WebContextLoaderListener继承了Spring的ContextLoaderListener类,ContextLoaderListener类是在容器启动时加载spring配置信息用的,web容器启动时,会加载contextInitialized()方法,在这里加载的就是WebContextLoaderListener类的contextInitialized()方法。

3,contextInitialized()方法调用了ContainerInitializer的startContainer()方法,参数是一个AbstractInitializeCallbackAdapter类,先看一下这个AbstractInitializeCallbackAdapter类:

package com.dangdang.ddframe.container.spring.initialize;

public abstract class AbstractInitializeCallbackAdapter implements InitializeCallback {

   

    @Override

    public void logSuccess(final Logger log) {

        log.info("--- dd-frame container started success. ---");

    }

   

    @Override

    public void logFailure(final Logger log, final SystemException cause) {

        log.ERROR("--- dd-frame init failure. ---", cause);

    }

}

其实就是定义了启动成功时候的日志和启动失败时候的日志,他实现的接口InitializeCallback定义了这两个方法,还有启动容器的doInitialize()方法。

4,现在开始看ContainerInitializer的startContainer()方法:

package com.dangdang.ddframe.container.spring.initialize;

    public void startContainer(final InitializeCallback initializeCallback) {

        prepareSystemProperties();

        ApplicationContext context = null;

        try {

            context = initializeCallback.doInitialize();

        // CHECKSTYLE:OFF

        } catch (final Exception ex) {

        // CHECKSTYLE:ON

            logAndthrow(initializeCallback, ex);

        }

        for (Entry<String, Initializevalidator> entry : context.getBeansOfType(InitializeValidator.class).entrySet()) {

            try {

                entry.getValue().validate();

            } catch (final InitializeValidatorException ex) {

                logAndThrow(initializeCallback, ex);

            }

        }

        ContextHolder.initInstance(context);

        initializeCallback.logSuccess(LOG);

       LOG_CONFIG.setLevel(CLASSPATH_PROP_LOADER.getClasspathProperties().getProperty(LogConfig.RUNNING_LOG_LEVEL_KEY));

}

5,prepareSystemProperties();方法是把配置文件中的配置项放到java.lang.System中。

然后调用了InitializeCallback的doInitialize()方法,就是在WebContextLoaderListener之中写的那一部分:

@Override

public ApplicationContext doInitialize() {

    ServletContext servletContext = event.getServletContext();

    servletContext.setInitParameter(ContextLoader.CONFIG_LOCATION_PARAM,SpringContainer.CONFIG_FILE);

    WebContextLoaderListener.super.contextInitialized(event);

    return WebApplicationContextUtils.getWebApplicationContext(servletContext);

}

首先设置了配置文件路径:把ContextLoader.CONFIG_LOCATION_PARAM设置为SpringContainer.CONFIG_FILE,这个路径是可配置的,这里配置为:"classpath:META-INF/dd-frame/spring/root/applicationContext.xml"。

然后,在这个方法中调用了父类的contextInitialized()方法,也就是org.springframework.web.context .ContextLoaderListener的contextInitialized()方法:

public void contextInitialized(ServletContextEvent event) {

         initWebApplicationContext(event.getServletContext());

}

6,其中调用的initWebApplicationContext()方法在他的父类org.springframework.web.context.ContextLoader中:

package org.springframework.web.context;

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {

                   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!");

                   }



                   Log logger = LogFactory.getLog(ContextLoader.class);

                   servletContext.log("Initializing Spring root WebApplicationContext");

                   if (logger.isInfoEnabled()) {

                            logger.info("Root WebApplicationContext: initialization started");

                   }

                   long startTime = system.currenttimemillis();



                   try {

                            // Store context in local instance variable, to guarantee that

                            // it is available on ServletContext shutdown.

                            if (this.context == null) {

                                     this.context = createWebApplicationContext(servletContext);

                            }

                            if (this.context instanceof ConfigurableWebApplicationContext) {

                                     ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;

                                     if (!cwac.isActive()) {

                                               // The context has not yet been refreshed -> provide services such as

                                               // setting the parent context, setting the application context id, etc

                                               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);

                                               }

                                               configureAndRefreshWebApplicationContext(cwac, servletContext);

                                     }

                            }

                            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);



                            ClassLoader ccl = Thread.currentThread().getContextClassLoader();

                            if (ccl == ContextLoader.class.getClassLoader()) {

                                     currentContext = this.context;

                            }

                            else if (ccl != null) {

                                     currentContextPerThread.put(ccl, this.context);

                            }



                            if (logger.isDebugEnabled()) {

                                     logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +

                                                        WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");

                            }

                            if (logger.isInfoEnabled()) {

                                     long elapsedTime = System.currentTimeMillis() - startTime;

                                     logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");

                            }



                            return this.context;

                   }

                   catch (runtimeexception ex) {

                            logger.error("Context initialization failed", ex);

                            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);

                            throw ex;

                   }

                   catch (Error err) {

                            logger.error("Context initialization failed", err);

                            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);

                            throw err;

                   }

         }

7,这个方法就是把spring配置文件中的内容加载到上下文的方法,初始化时this.context为null,调用createWebApplicationContext()方法:

         protected WebApplicationContext createWebApplicationContext(ServletContext sc) {

                   Class<?> contextClass = determineContextClass(sc);

                   if (!ConfigurableWebApplicationContext.class.isassignableFrom(contextClass)) {

                            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +

                                               "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");

                   }

                   return (ConfigurableWebApplicationContext) beanutils.instantiateClass(contextClass);

         }

8,方法的第一行调用determineContextClass()方法,返回了ConfigurableWebApplicationContext的Class类,这个方法有很多层,看到最后是调用了ContextLoader.properties配置文件,这个配置文件在Spring的包中是和ContextLoader.class文件放在一起的,没有开放给用户使用,不可配置,这个配置文件内容如下:

# Default WebApplicationContext implementation class for ContextLoader.

# Used as fallback when no explicit context implementation has been specified as context-param.

# Not meant to be customized by application developers.



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

如果去掉注释,就一行,代表这里spring的上下文实际上默认是用XmlWebApplicationContext来初始化的。注释里也写了,没打算开放给开发人员去配置。

9,回头看步骤7,在得到Class类后,最后一行调用BeanUtils.instantiateClass()方法进行初始化,并返回。

10,继续看步骤6中的代码,在刚才调用createWebApplicationContext()方法得到对象后,调用configureAndRefreshWebApplicationContext()方法来加载上下文配置:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
          if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
                   // The application context id is still set to its original default value
                   // -> assign a more useful id based on available information
                   String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
                   if (idParam != null) {
                            wac.setId(idParam);
                   }
                   else {
                            // Generate default id...
                            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                                               ObjectUtils.getdisplayString(sc.getcontextpath()));
                   }
          }

          wac.setServletContext(sc);
          String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
          if (configLocationParam != null) {
                   wac.setConfigLocation(configLocationParam);
          }

          // The wac environment's #initPropertySources will be called in any case when the context
          // is refreshed; do it eagerly here to ensure servlet property sources are in place for
          // use in any post-processing or initialization that occurs below prior to #refresh
          ConfigurableEnvironment env = wac.getEnvironment();
          if (env instanceof ConfigurableWebEnvironment) {
                   ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
          }

          customizeContext(sc, wac);
          wac.refresh();
}

11,代码最后的refresh()方法,实际调用的是之前初始化的XmlWebApplicationContext的refresh()方法,这个方法的实现在他的祖辈中,这个类的继承关系是:

org.springframework.web.context.support.XmlWebApplicationContext

继承

org.springframework.web.context.support.AbstractRefreshableWebApplicationContext

继承

org.springframework.context.support.AbstractRefreshableConfigApplicationContext

继承

org.springframework.context.support.AbstractRefreshableApplicationContext

继承

org.springframework.context.support.AbstractApplicationContext

refresh()方法在AbstractApplicationContext类中:

public void refresh() throws BeansException, IllegalStateException {
          synchronized (this.startupShutdownmonitor) {
                   // Prepare this context for refreshing.
                   prepareRefresh();

                   // Tell the subclass to refresh the internal bean factory.
                   ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

                   // Prepare the bean factory for use in this context.
                   prepareBeanFactory(beanFactory);

                   try {
                            // Allows post-processing of the bean factory in context subclasses.
                            postProcessBeanFactory(beanFactory);

                            // Invoke factory processors registered as beans in the context.
                            invokeBeanFactoryPostProcessors(beanFactory);

                            // Register bean processors that intercept bean creation.
                            registerBeanPostProcessors(beanFactory);

                            // Initialize message source for this context.
                            initMessageSource();

                            // Initialize event multicaster for this context.
                            initApplicationEventMulticaster();

                            // Initialize other special beans in specific context subclasses.
                            onRefresh();

                            // Check for listener beans and register them.
                            registerListeners();

                            // Instantiate all remaining (non-lazy-init) singletons.
                            finishBeanFactoryInitialization(beanFactory);

                            // Last step: publish corresponding event.
                            finishRefresh();
                   }

                   catch (BeansException ex) {
                            logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);

                            // Destroy already created singletons to avoid dangling resources.
                            destroyBeans();

                            // Reset 'active' flag.
                            cancelRefresh(ex);

                            // Propagate exception to caller.
                            throw ex;
                   }
          }
}

可以看到refresh()方法初始化了一个BeanFactory,这个方法进行了一系列对BeanFactory的初始化和装配工作,每个子方法里都进行了大量的操作,直到finishBeanFactoryInitialization()方法,初始化了所有非懒加载的单例bean的实例。

至此spring的上下文加载完成。

相关阅读

皮皮播放器右边显示分类信息加载中一直无法显示解决办

迷你页,即播放器右侧的播放列表页,现在有一个窄版和一个宽版,问题的源头相同,解决办法也差不多。迷你页显示不了一般指打开播放器以后

自动批量删除全部微博(自动翻页加载)

批量删除自己的全部微博 使用Chrome浏览器 登陆并打开微博首页-选择微博( 个人微博列表页面:https://www.weibo.com/YOUR_ID/profi

AssetBundle的如何加载

Assets:  apk/assetsStreamingAssets : 这个文件夹   在   手机平台  一般是只读的,不可写,安卓打包到上麦呢的文件夹persisten

网站速度慢怎么优化?网站加载速度优化方法

5个优化页面加载速度提高SEO排名的最佳实践客户和用户总是在他们的手机上寻找信息 &ndash; 他们希望快速!对速度的需求推动了他们

深入理解@Autowired注解以及Spring加载Bean的机制

@Autowired注解在平时开发中用的非常的多,即自动装配,这些天碰到了一个与之相关的报错,所以打算深入理解其原理。 首先看看它的定

分享到:

栏目导航

推荐阅读

热门阅读