Spring源码情操陶冶-ContextLoader

前言-阅读源码有利于陶冶情操,本文承接前文Spring源码情操陶冶-ContextLoaderListener

静态代码块内容

ContextLoader在被主动调用的时候,会执行其的一个静态块,代码如下

static {
        // Load default strategy implementations from properties file.
        // This is currently strictly internal and not meant to be customized
        // by application developers.
        try {
                //这里DEFAULT_STRATEGIES_PATH值为ContextLoader.properties
            ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
            //这里的loadProperties方法其实调用的getClass().getResourceAsStream(path)方法
            //其是从当前包路径下查找相应的资源,点开相应的class路径,果不其然ContextLoader.properties与ContextLoader.class在同一目录下
            defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
        }
        catch (IOException ex) {
            throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
        }
    }

具体的解析功能见注释,上面分析到其会读取ContextLoader.properties,这个文件下只有一个属性:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

由此可见其最有可能是通过XmlWebApplicationContext类来进行相应的web上下文初始化

初始化方法initWebApplicationContext

从前文得知,ContextLoaderListener会调用ContextLoader#initWebApplicationContext(ServletContext context)方法来创建web application上下文环境,代码清单如下

//首先判断有无org.springframework.web.context.WebApplicationContext.ROOT属性,不允许已有
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) {
                //创建方法,一般会创建前点所述的XmlWebApplicationContext
                this.context = createWebApplicationContext(servletContext);
            }
            //XmlWebApplicationContext是ConfigurableWebApplicationContext的实现类,指定的contextClass应该是ConfigurableWebApplicationContext的实现类
            if (this.context instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
                //一般来说刚创建的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.
                        //在web.xml中配置了<context-param>的parentContextKey才会指定父级应用
                        ApplicationContext parent = loadParentContext(servletContext);
                        cwac.setParent(parent);
                    }
                    //读取相应的配置并且刷新context对象
                    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;
        }

上述的代码片段中主要用到了两个关键方法createWebApplicationContext(ServletContext context)configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac,ServletContext sc),本文必须对这两个方法进行相应的解读

ContextLoader#createWebApplicationContext(ServletContext context)

代码清单如下

                //决定采用默认的contextClass还是ServletContext中的参数属性contextClass
                Class<?> contextClass = determineContextClass(sc);
        //这里Spring强调指定的contextClass必须是ConfigurableWebApplicationContext的实现类或者子类
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                    "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        }
        //这里就是实例化指定的contextClass
        return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

实现的主要功能为创建ConfigurableWebApplicationContext对象,这里可以稍微瞄一眼determineContextClass(ServletContext context)

            //优先从ServletContext取contextClass参数对应的值,即查看是否在web.xml中配置
            //对应的contextClass<context-param>参数值
            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 {
            //倘若不存在,则加载指定的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);
            }
        }

ContextLoader#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
            //获取servletContext中的contextId属性
            String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
            if (idParam != null) {
                //存在则设为指定的id名
                wac.setId(idParam);
            }
            else {
                // Generate default id... 一般为org.springframework.web.context.WebApplicationContext:${contextPath}
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                        ObjectUtils.getDisplayString(sc.getContextPath()));
            }
        }

        wac.setServletContext(sc);
        //读取contextConfigLocation属性
        String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
        if (configLocationParam != null) {
            //设置指定的spring文件所在地,支持classpath前缀并多文件,以,;为分隔符
            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
        //获得真实对象为StandardEnvironment 其非ConfigurableWebEnvironment的实现类
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
        }
        //查看是否有ApplicationContextInitializer<C extends ConfigurableApplicationContext>启动类,其实是web.xml需要指定globalInitializerClasses参数或者contextInitializerClasses参数,前提是指定的这些类的泛型类必须是wac的父类或者与wac类相同,否则就会有异常抛出
        customizeContext(sc, wac);
        //调用AbstractApplicationContext的refresh方法实现加载spring文件等操作
        wac.refresh();
}

可见关键处理还是在AbstractApplicationContext#refresh()方法,前面都是refresh()方法的准备工作,包括指定contextConfigLocationSpring配置文件位置、给应用一个id倘若指定了contextId属性、如果指定了globalInitializerClasses或者contextInitializerClasses参数则调用其中的initialize()方法

记录总结

  1. ContextLoader在web.xml配置文件中没有指定contextClass的属性下会默认加载XmlWebApplicationContext类,如果指定了contextClass属性,Spring强调指定的contextClass必须是ConfigurableWebApplicationContext的实现类或者子类

  2. web.xml配置文件中如果没有指定contextId属性,则WebApplicationContext的id为org.springframework.web.context.WebApplicationContext:${contextPath},其中${contextPath}是项目的上下文路径,例如localhost:8080/test/info/query路径下的contextPath为/test
  3. web.xml配置的contextConfigLocation属性支持多文件,以,;为分隔符,不指定默认会加载applicationContext.xml,其在后续章节剖析

  4. web.xml倘若指定globalInitializerClasses参数或者contextInitializerClasses参数,具体要求如下
    • 指定的类实现ApplicationContextInitializer<C extends ConfigurableApplicationContext>接口
    • 指定的这些类中的泛型类必须是contextClass(默认为XmlWebApplicationContext)的父类或者一致,否则就会有异常抛出

下节预告

Spring源码情操陶冶-AbstractApplicationContext

转载于:https://www.cnblogs.com/question-sky/p/6694627.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值