@(spring 源碼系列)
介紹
ContextLoader類被ContextLoaderListener類調(diào)用唇礁,來初始化application context上下文。ContextLoaderListener這個(gè)類你一定見過苍苞,如果你寫過spring mvc項(xiàng)目的話。他一定出現(xiàn)在web.xml文件里
解析從程序執(zhí)行的邏輯出發(fā)狼纬,一步步來分析羹呵。這樣做有助于理清程序邏輯,對(duì)后續(xù)遇到的問題定位也有很大幫助
時(shí)序圖
方法調(diào)用順序
- initWebApplicationContext
if (this.context == null) {
//創(chuàng)建web容器上下文
this.context = createWebApplicationContext(servletContext);
}
- createWebApplicationContext
//返回WebApplicationContext接口的實(shí)現(xiàn)類疗琉。默認(rèn)返回XmlWebApplicationContext冈欢,或者返回指定的實(shí)現(xiàn)類
Class<?> contextClass = determineContextClass(sc);
- determineContextClass
//獲取自定義WebApplicationContext上下文實(shí)現(xiàn)類。指定實(shí)現(xiàn)類的指定方式在web.xml里通過參數(shù)contextClass指定
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
if (contextClassName != null) {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}else{
//沒有指定實(shí)現(xiàn)類盈简,默認(rèn)返回XmlWebApplicationContext實(shí)現(xiàn)類凑耻。默認(rèn)實(shí)現(xiàn)類配置在classpath下的ContextLoader.properties里
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
- createWebApplicationContext
//利用jdk反射太示,創(chuàng)建上下文實(shí)例.實(shí)例對(duì)象中有一個(gè)唯一標(biāo)識(shí)id,這里的id生成的時(shí)機(jī)發(fā)生在類AbstractApplicationContext的成員變量private String id = ObjectUtils.identityToString(this);
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
- initWebApplicationContext
//配置和刷新容器上下文香浩,包括解析xml文件类缤,加載bean,調(diào)用beanfactorypostprocessor邻吭,beanpostprocesssor
configureAndRefreshWebApplicationContext(cwac, servletContext);
//把當(dāng)前的webapplication上下文設(shè)置到servlet上下文中餐弱。這里的key是WebApplicationContext接口的全限定名
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
- configureAndRefreshWebApplicationContext
//設(shè)置容器的唯一標(biāo)識(shí)id
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
獲取web.xml文件參數(shù)contextId配置的容器標(biāo)識(shí)id
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
//生成默認(rèn)的的容器id= org.springframework.web.context.WebApplicationContext:
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
//設(shè)置servlet上下文到容器中
wac.setServletContext(sc);
//獲取spring配置文件未知并設(shè)置到容器中
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
//創(chuàng)建容器的servlet環(huán)境
ConfigurableEnvironment env = wac.getEnvironment();
//初始化容器的servlet的環(huán)境,將servletContext和servletConfig設(shè)置到容器的servlet環(huán)境中
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
//自定制容器上下文.通過ApplicationContextInitializer初始化器來實(shí)現(xiàn)定制化囱晴。
//我們可以通過web.xml配置初始化類膏蚓,多個(gè)初始化器可通過@order注解來指定初始化順序
customizeContext(sc, wac);
// 刷新容器上下文
wac.refresh();
- customizeContext
// 獲取上下文初始化器,初始化器可通過web.xml配置
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
determineContextInitializerClasses(sc);
for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
Class<?> initializerContextClass =
GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
}
// 根據(jù)注解@order排序初始化器
AnnotationAwareOrderComparator.sort(this.contextInitializers);
// 回調(diào)初始化器速缆,定制容器上下文
for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitia