詳解contextConfigLocation|Spring啓動過程詳解(轉)

原文連接:https://blog.csdn.net/qw222pzx/article/details/78191670html

spring的應用初始化流程一直沒有搞明白,剛剛又碰到了相關的問題。決定得好好看看這個流程。咱們在開發spring的項目當中基本上都會在web.xml經過:java

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

來初始化各個spring的配置文件,可是咱們只是知道這段代碼的功能, 並非很清楚咱們配置了這段代碼以後爲何就能去初始化配置文件。固然咱們還會加上:web

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

這一個listener,我首先就會想contextConfigLocation這個必定能在ContextLoaderListener這個類當中找到,打開了源碼,這個listener是實現了ServletContextListener這個接口的,這個接口只有兩個方法:spring

複製代碼
public interface ServletContextListener  
    extends EventListener  
{  
  
    public abstract void contextInitialized(ServletContextEvent servletcontextevent);  
  
    public abstract void contextDestroyed(ServletContextEvent servletcontextevent);  
}  
複製代碼

並且它是繼承了EventListener這個接口的,打開這個接口的代碼讓我大吃一驚,裏面沒有方法啥都沒有:api

複製代碼
package java.util;  
  
  
public interface EventListener  
{  
}  
複製代碼

並且仍是java.util包下的,並非spring之中的東西。app

 

這樣找了以後沒有找到,往回退到ContextLoaderListener這個類的方法上,contextInitialized方法是用來初始化上下文的:this

public void contextInitialized(ServletContextEvent event)  
    {  
        contextLoader = createContextLoader();  
        contextLoader.initWebApplicationContext(event.getServletContext());  
    }  

方法中有個createContextLoader方法:spa

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

這個方法返回了一個ContextLoader實例,進入到ContextLoader類中,按ctrl+f來尋找contextConfigLocation,這時沒有出現電腦的咚的聲音,找到了它:.net

複製代碼
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方法:code

複製代碼
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);  

pring使用BeanUtils來初始化contextClass這個類實例,contextClass是經過如下代碼獲得的:

複製代碼
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 這個方法中,咱們主要看:

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的反射使用範圍之廣再次體現出來。如看了以後以爲有錯誤或者不一樣意見,歡迎提出來,我也是第一次才研究這個問題。

相關文章
相關標籤/搜索