spring的應(yīng)用初始化流程一直沒有搞明白,,剛剛又碰到了相關(guān)的問題。決定得好好看看這個(gè)流程,。我們在開發(fā)spring的項(xiàng)目當(dāng)中基本上都會在web.xml通過:
-
<context-param>
-
<param-name>contextConfigLocation</param-name>
-
<param-value>
-
/WEB-INF/conf/application-*.xml
-
</param-value>
-
</context-param>
來初始化各個(gè)spring的配置文件,,但是我們只是知道這段代碼的功能, 并不是很清楚我們配置了這段代碼之后為什么就能去初始化配置文件,。當(dāng)然我們還會加上:
-
<listener>
-
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
-
</listener>
這一個(gè)listener,,我首先就會想contextConfigLocation這個(gè)一定能在ContextLoaderListener這個(gè)類當(dāng)中找到,打開了源碼,,這個(gè)listener是實(shí)現(xiàn)了ServletContextListener這個(gè)接口的,,這個(gè)接口只有兩個(gè)方法:
-
public interface ServletContextListener
-
extends EventListener
-
{
-
-
public abstract void contextInitialized(ServletContextEvent servletcontextevent);
-
-
public abstract void contextDestroyed(ServletContextEvent servletcontextevent);
-
}
而且它是繼承了EventListener這個(gè)接口的,打開這個(gè)接口的代碼讓我大吃一驚,,里面沒有方法啥都沒有:
-
package java.util;
-
-
-
public interface EventListener
-
{
-
}
而且還是java.util包下的,,并不是spring之中的東西。
這樣找了之后沒有找到,,往回退到ContextLoaderListener這個(gè)類的方法上,contextInitialized方法是用來初始化上下文的:
-
public void contextInitialized(ServletContextEvent event)
-
{
-
contextLoader = createContextLoader();
-
contextLoader.initWebApplicationContext(event.getServletContext());
-
}
方法中有個(gè)createContextLoader方法:
-
protected ContextLoader createContextLoader()
-
{
-
return new ContextLoader();
-
}
這個(gè)方法返回了一個(gè)ContextLoader實(shí)例,,進(jìn)入到ContextLoader類中,,按ctrl f來尋找contextConfigLocation,這時(shí)沒有出現(xiàn)電腦的咚的聲音,,找到了它:
-
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設(shè)置了從servletContext獲取到的參數(shù)的值,再進(jìn)入ConfigurableWebApplicationContext的代碼中,,它只是一個(gè)接口,,進(jìn)入StaticWebApplicationContext的setConfigLocation方法:
-
public void setConfigLocation(String configLocation)
-
{
-
if(configLocation != null)
-
throw new UnsupportedOperationException('StaticWebApplicationContext does not support config locations');
-
else
-
return;
-
}
這個(gè)方法中很奇怪,,當(dāng)參數(shù)不為空就拋出異常,查看spring的文檔:The StaticWebApplicationContext class
does not support this method.說是此類不支持這個(gè)方法,,這下子又卡住了,。又要退回去,看這句:
-
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
spring使用BeanUtils來初始化contextClass這個(gè)類實(shí)例,,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的構(gòu)造方法。下面是instantiateClass方法的重載,,主要是下面兩句代碼:
-
ReflectionUtils.makeAccessible(ctor);
-
return ctor.newInstance(args);
ctor是通過反射得到的contextClass的構(gòu)造方法,,args是構(gòu)造方法當(dāng)中的參數(shù)。這里為null,,說明new了contextClass的無參構(gòu)造方法,。
這時(shí)又要退回到determineContextClass 這個(gè)方法中,我們主要看:
-
contextClassName = defaultStrategies.getProperty((org.springframework.web.context.WebApplicationContext.class).getName());
這句代碼,,我們可以猜它是通過Properties的getProperty方法得到WebApplicationContext 的實(shí)例,,這時(shí)我們又到了WebApplicationContext
這個(gè)接口當(dāng)中,這個(gè)接口繼承了ApplicationContext這個(gè)接口,,我們都知道我們進(jìn)行spring開發(fā)都會通過Application ctx=new FileSystemXmlApplicationContext('beans.xml');或ApplicationContext
ctx=new ClassPathXmlApplicationContext('beans.xml');或ServletContext servletContext = request.getSession().getServletContext();ApplicationContext
ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);這三種方法獲得一個(gè)ApplicationContext,,然后就可以對配置文件當(dāng)中的bean進(jìn)行操作了。所以這里我們基本上已經(jīng)搞清楚初始化spring配置文件的流程了,。
總結(jié):通過查看這幾個(gè)類的源代碼,,java的反射使用范圍之廣再次體現(xiàn)出來。如看了之后覺得有錯(cuò)誤或者不同意見,,歡迎提出來,,我也是第一次才研究這個(gè)問題。
|