spring的應用初始化流程一直沒有搞明白,剛剛又碰到了相關的問題。決定得好好看看這個流程。咱們在開發spring的項目當中基本上都會在web.xml經過:html
[html] view plaincopy <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/conf/application-*.xml
</param-value>
</context-param>
來初始化各個spring的配置文件,可是咱們只是知道這段代碼的功能, 並非很清楚咱們配置了這段代碼以後爲何就能去初始化配置文件。固然咱們還會加上: [html] view plaincopy <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
這一個listener,我首先就會想contextConfigLocation這個必定能在ContextLoaderListener這個類當中找到,打開了源碼,這個listener是實現了ServletContextListener這個接口的,這個接口只有兩個方法: [html] view plaincopy public interface ServletContextListener
extends EventListener
{java
public abstract void contextInitialized(ServletContextEvent servletcontextevent); public abstract void contextDestroyed(ServletContextEvent servletcontextevent);
}
並且它是繼承了EventListener這個接口的,打開這個接口的代碼讓我大吃一驚,裏面沒有方法啥都沒有: [html] view plaincopy package java.util;web
public interface EventListener
{
}
並且仍是java.util包下的,並非spring之中的東西。 這樣找了以後沒有找到,往回退到ContextLoaderListener這個類的方法上,contextInitialized方法是用來初始化上下文的:spring
[html] view plaincopy public void contextInitialized(ServletContextEvent event)
{
contextLoader = createContextLoader();
contextLoader.initWebApplicationContext(event.getServletContext());
}
方法中有個createContextLoader方法: [html] view plaincopy protected ContextLoader createContextLoader()
{
return new ContextLoader();
}
這個方法返回了一個ContextLoader實例,進入到ContextLoader類中,按ctrl+f來尋找contextConfigLocation,這時沒有出現電腦的咚的聲音,找到了它: [html] view plaincopy 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方法: [html] view plaincopy 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.說是此類不支持這個方法,這下子又卡住了。又要退回去,看這句:app
[html] view plaincopy ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
spring使用BeanUtils來初始化contextClass這個類實例,contextClass是經過如下代碼獲得的:this
[html] view plaincopy 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方法:spa
[html] view plaincopy return instantiateClass(clazz.getDeclaredConstructor((Class[])null), null);
經過反射獲得contextClass的構造方法。下面是instantiateClass方法的重載,主要是下面兩句代碼:code
[html] view plaincopy ReflectionUtils.makeAccessible(ctor);
return ctor.newInstance(args);
ctor是經過反射獲得的contextClass的構造方法,args是構造方法當中的參數。這裏爲null,說明new了contextClass的無參構造方法。 這時又要退回到determineContextClass 這個方法中,咱們主要看:xml
[html] view plaincopy 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的反射使用範圍之廣再次體現出來。如看了以後以爲有錯誤或者不一樣意見,歡迎提出來,我也是第一次才研究這個問題。htm