详解contextConfigLocation

详解contextConfigLocationspring的应用初始化流程一直没有搞明白,刚刚又碰到了相关的问题。决定得好好看看这个流程。我们在开发spring的项目当中基本上都会在web.xml通过:来初始化各个spring的配置文件,但是我们只是知道这段代码的功能,并不是很清楚我们配置了这段代码之后为什么就能去初始化配置文件。当然我们还会加上:listener>          li

大家好,又见面了,我是你们的朋友全栈君。

spring的应用初始化流程一直没有搞明白,刚刚又碰到了相关的问题。决定得好好看看这个流程。我们在开发spring的项目当中基本上都会在web.xml通过:

<context-param>          <param-name>contextConfigLocation</param-name>          <param-value>          /WEB-INF/conf/application-*.xml          </param-value>      </context-param>  

来初始化各个spring的配置文件,但是我们只是知道这段代码的功能, 并不是很清楚我们配置了这段代码之后为什么就能去初始化配置文件。当然我们还会加上:

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

这一个listener,我首先就会想contextConfigLocation这个一定能在ContextLoaderListener这个类当中找到,打开了源码,这个listener是实现了ServletContextListener这个接口的,这个接口只有两个方法:

  1. public interface ServletContextListener  
        extends EventListener  
    {  
      
        public abstract void contextInitialized(ServletContextEvent servletcontextevent);  
      
        public abstract void contextDestroyed(ServletContextEvent servletcontextevent);  
    } 

而且它是继承了EventListener这个接口的,打开这个接口的代码让我大吃一惊,里面没有方法啥都没有:

  1. package java.util;  
    
    public interface EventListener  
    {  
    } 

     

而且还是java.util包下的,并不是spring之中的东西。

这样找了之后没有找到,往回退到ContextLoaderListener这个类的方法上,contextInitialized方法是用来初始化上下文的:

  1. public void contextInitialized(ServletContextEvent event)  
        {  
            contextLoader = createContextLoader();  
            contextLoader.initWebApplicationContext(event.getServletContext());  
        } <span style="font-family: 宋体, Arial; background-color: rgb(255, 255, 255);"> </span>

方法中有个createContextLoader方法:

  1. protected ContextLoader createContextLoader()  
        {  
            return new ContextLoader();  
        }  

这个方法返回了一个ContextLoader实例,进入到ContextLoader类中,按ctrl+f来寻找contextConfigLocation,这时没有出现电脑的咚的声音,找到了它:

  1. protected WebApplicationContext createWebApplicationContext(ServletContext servletContext, ApplicationContext parent)  
            throws BeansException  
        {  
            Class contextClass = determineContextClass(servletContext);  
            if(!(org.springframework.web.context.ConfigurableWebApplicationContext.class).isAssignableFrom(contextClass))  
            {  
                throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + (org.springframework.web.context.ConfigurableWebApplicationContext.class).getName() + "]");  
            } else  
            {  
                ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);  
                wac.setParent(parent);  
                wac.setServletContext(servletContext);  
                wac.setConfigLocation(servletContext.getInitParameter("<SPAN style="COLOR: #ff0000">contextConfigLocation</SPAN>"));  
                customizeContext(servletContext, wac);  
                wac.refresh();  
                return wac;  
            }  
        }  

通过代码,ConfigurableWebApplicationContext设置了从servletContext获取到的参数的值,再进入ConfigurableWebApplicationContext的代码中,它只是一个接口,进入StaticWebApplicationContext的setConfigLocation方法:
 
view plain
copy
print
?

  1. public void setConfigLocation(String configLocation)  
        {  
            if(configLocation != null)  
                throw new UnsupportedOperationException("StaticWebApplicationContext does not support config locations");  
            else  
                return;  
        }  

这个方法中很奇怪,当参数不为空就抛出异常,查看spring的文档:
The 
StaticWebApplicationContext
 class does not support this method.说是此类不支持这个方法,这下子又卡住了。又要退回去,看这句:

ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass)

spring使用BeanUtils来初始化contextClass这个类实例,contextClass是通过以下代码得到的:

  1. protected Class determineContextClass(ServletContext servletContext)  
            throws ApplicationContextException  
        {  
            String contextClassName = servletContext.getInitParameter("contextClass");  
            if(contextClassName != null)  
                try  
                {  
                    return ClassUtils.forName(contextClassName);  
                }  
                catch(ClassNotFoundException ex)  
                {  
                    throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", ex);  
                }  
            contextClassName = defaultStrategies.getProperty((org.springframework.web.context.WebApplicationContext.class).getName());  
            try  
            {  
                return ClassUtils.forName(contextClassName, (org.springframework.web.context.ContextLoader.class).getClassLoader());  
            }  
            catch(ClassNotFoundException ex)  
            {  
                throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", ex);  
            }  
        }  

这里使用了反射,再来看BeanUtils的instantiateClass方法:

return instantiateClass(clazz.getDeclaredConstructor((Class[])null), null);  

通过反射得到contextClass的构造方法。下面是instantiateClass方法的重载,主要是下面两句代码:

ReflectionUtils.makeAccessible(ctor);  
            return ctor.newInstance(args);  

ctor是通过反射得到的contextClass的构造方法,args是构造方法当中的参数。这里为null,说明new了contextClass的无参构造方法。

这时又要退回到determineContextClass 这个方法中,我们主要看:

[html] 
view plain
copy
print
?

  1. contextClassName = defaultStrategies.getProperty((org.springframework.web.context.WebApplicationContext.class).getName());  

这句代码,我们可以猜它是通过Properties的getProperty方法得到
WebApplicationContext 的实例,这时我们又到了WebApplicationContext 这个接口当中,这个接口继承了ApplicationContext这个接口,我们都知道我们进行spring开发都会通过Application ctx=new FileSystemXmlApplicationContext(“beans.xml”);或ApplicationContext ctx=new ClassPathXmlApplicationContext(“beans.xml”);ServletContext servletContext = request.getSession().getServletContext();ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);这三种方法获得一个ApplicationContext,然后就可以对配置文件当中的bean进行操作了。所以这里我们基本上已经搞清楚初始化spring配置文件的流程了。


总结:通过查看这几个类的源代码,java的反射使用范围之广再次体现出来。如看了之后觉得有错误或者不同意见,欢迎提出来,我也是第一次才研究这个问题。


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/158024.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • WIFI 常识

    WIFI 常识DSSS(DirectSequenceSpreadSpectrum)直接序列扩频FHSS,跳频技术(Frequency-HoppingSpreadSpectrum)FHSS和DSSS比较跳频扩频(FHSS):跳频扩频(FHSS)技术是通过“伪随机码”的调制,信息的载波受一伪随机序列的控制,使载波工作的中心频率不断跳跃改变,而噪音和干扰信号的中心频率却不会改变,这样,只要收、发信机之间按照固定的数字算法产生相同的“伪随机码”,就可以达到同步,排除了噪音和其它干扰信号。虽然在..

    2022年7月20日
    13
  • 精美的液晶数字字体素材[通俗易懂]

    精美的液晶数字字体素材[通俗易懂]液晶数字应该比较常见,那么液晶数字字体的应用也是相对广泛了,可以运用于一切需要液晶显示屏上的数字字体显示。对于这样一种有着广泛的应用数字字体,选择使用哪款液晶数字字体也是一个很重要的问题啦!为此,特意为大家收集了几款液晶数字字体供大家选择,喜欢的朋友赶紧收藏起来吧!  DS-Digital字体是一款比较常规的液晶数字字体,这款字体的仅支持数字和大写字母输入,字体端正,结构完整,整体视觉呈现效果…

    2025年7月27日
    5
  • UDP协议开发

    UDP协议开发1简介在进行电网插件开发的过程中,对电网接入程序进行了开发,使得在综合安防管理平台上能够非常方便的接入天地维正电网设备。电网数据采用UDP协议,通过监狱局域网,向用户指定的5个IP地址的某端口,同时发送,各IP地址收到的数据相同。因为是第一次使用网络数据报进行开发,因此遇到了许多的坑。在这里把遇到的问题组织成一个文档,重新理解在代码撰写过程中遇到的问题。本文档适用于初次使用UDP进行…

    2022年5月31日
    73
  • 史上最详细Git使用教程[通俗易懂]

    史上最详细Git使用教程[通俗易懂]本篇为廖雪峰老师官方网站中Git教程的学习笔记Git是世界上最先进的分布式版本控制系统,克隆一个项目的速度非常快每个开发都可以从master上克隆一个本地版本库,就算没有网络,也可以提交代码到本地仓库、查看log、创建项目分支等等每个版本库都可以创建无限个分支,分支是个完整的目录,且这个目录拥有完整的实际文件一、安装网上搜一下安装教程,这里就不介绍了安装完成后,在开始菜单里…

    2022年4月30日
    77
  • 计算机程序的构造和解释——笔记(一)

    计算机程序的构造和解释——笔记(一)

    2021年7月9日
    70
  • 接口测试题目_jmeter接口自动化测试框架

    接口测试题目_jmeter接口自动化测试框架http接口工具有很多如:postman、jmeter、soupUI、Java+httpclient、robotframework+httplibrary、Fidder+postman等1、什么是接口接口就是内部模块对模块,外部系统对其他服务提供的一种可调用或者连接的能力的标准接口的种类和分类:webservice和httpapi接口1)webservice接口是走soap协议通过htt…

    2022年9月27日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号