博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring源码情操陶冶-ContextLoader
阅读量:4496 次
发布时间:2019-06-08

本文共 10276 字,大约阅读时间需要 34 分钟。

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

静态代码块内容

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中配置了
的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
参数值 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
启动类,其实是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

你可能感兴趣的文章
Chrome的hack写法以及CSS的支持程度图示
查看>>
苹果端手机微信页面长按图片无法保存的解决方案
查看>>
iOS 常用四种数据存储方式
查看>>
System.Web.Routing入门及进阶 下篇
查看>>
没有方向是多么可怕
查看>>
个人随笔之《寻找真实的自己》
查看>>
VS2017常用快快捷键
查看>>
docker问题
查看>>
velocity.js的使用
查看>>
前端网址导航-最酷前端圈子在这里
查看>>
SVN、GIT比较
查看>>
asp后台读id设置样式
查看>>
VS2005编译GEOS GDAL
查看>>
奇虎360安全牛人全球挑战赛无线部…
查看>>
POJ 3744 Scout YYF I 概率DP+矩阵快速幂 好题
查看>>
Training Logisches Denken
查看>>
谁分配谁释放
查看>>
正则表达式
查看>>
Java集合之LinkedHashSet源码分析
查看>>
David Silver强化学习Lecture1:强化学习简介
查看>>