在spring框架存在的web项目中,需要在web.xml中配置
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
这个监听器,作用是初始化spring容器。
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
调用了ContextLoader的initWebApplicationContext()方法,这个方法关键有2步;第一步createWebApplicationContext,
第二步,this.configureAndRefreshWebApplicationContext(cwac, servletContext);
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
this.context = this.createWebApplicationContext(servletContext);
//xmlWebAplicationContext
if(this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
if(!cwac.isActive()) {
if(cwac.getParent() == null) {//第一次启动parent肯定是null的
ApplicationContext parent = this.loadParentContext(servletContext);
cwac.setParent(parent);//如果web.xml中没有配置locatorFactorySelector、parentContextKey,则parent还是null
}
//修改cwac的id org.springframework.web.context.support.XmlWebApplicationContext@5f98e77d为org.springframework.web.context.WebApplicationContext:
//设置ConfigLocation参数,在web.xml中有配置
//把servletcontext和ServletConfig放到environment中
//定制cwac
this.configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if(ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;//一般是这条线路
} else if(ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
return this.context;
} catch (RuntimeException var8) {
}
}
}
创建webContext,这一块比较简单,使用tomcat 的类加载器加载context的class,然后通过反射创建对象实例:
contextClass会使用默认的org.springframework.web.context.support.XmlWebApplicationContext,
也可以在web.xml中配置初始化参数contextClass来自定义。
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
Class<?> contextClass = this.determineContextClass(sc);
if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {//判断contextClass是不是ConfigurableWebApplicationContext.class的子类,必须是,不然抛异常
throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
} else {
return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);//实例化类,调用无参构造函数。
}
}
/**
*获取实际实例化的class
*/
protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter("contextClass");
if(contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
} catch (ClassNotFoundException var4) {
throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4);
}
} else {//没有配置contextClass参数的话,取默认的值,在ContextLoader.properties中取,默认值是org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
} catch (ClassNotFoundException var5) {
throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5);
}
}
}
第二步,配置并刷新web容器
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
String configLocationParam;
if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
//wac的id在初始化的时候被设置了,也是使用的objectUtils的算法,所以这里是相等的
//走这一步是替换初识的id
configLocationParam = sc.getInitParameter("contextId");
if(configLocationParam != null) {
wac.setId(configLocationParam);//有自定义,则替换成自定义的
} else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);//持有servletContext
configLocationParam = sc.getInitParameter("contextConfigLocation");
if(configLocationParam != null) {
//这里获取的是spring的配置文件地址
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
//env是StandardServletEnvironment的实例
if(env instanceof ConfigurableWebEnvironment) {
//StandardServletEnvironment是ConfigurableWebEnvironment的子类
((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);//这一步没做什么很重要的事情
}
this.customizeContext(sc, wac);//这一步也没什么
wac.refresh();//重点
}
关于定制cwac:
没有特殊配置的话,这个方法什么事情都不会干
protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = this.determineContextInitializerClasses(sc);//没有特殊配置,一般是空的
Iterator var4 = initializerClasses.iterator();
while(var4.hasNext()) {
Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass = (Class)var4.next();
Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
if(initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
throw new ApplicationContextException(String.format("Could not apply context initializer [%s] since its generic parameter [%s] is not assignable from the type of application context used by this context loader: [%s]", new Object[]{initializerClass.getName(), initializerContextClass.getName(), wac.getClass().getName()}));
}
this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
}
AnnotationAwareOrderComparator.sort(this.contextInitializers);
var4 = this.contextInitializers.iterator();
while(var4.hasNext()) {
ApplicationContextInitializer<ConfigurableApplicationContext> initializer = (ApplicationContextInitializer)var4.next();
initializer.initialize(wac);
}
}
所以在web应用中,spring项目的启动核心就是refresh方法:
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();//基本设置,可以忽略
//创建DefaultListableBeanFactory,设置ID为applicationContext的id
//loadBeanDefinitions,加载bean的定义,
//把所有的bean定义加载到Map<String, BeanDefinition> beanDefinitionMap
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//注册一些依赖的bean
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if(this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
其中obtainFreshBeanFactory();调用XmlWebApplicationContext的loadBeanDefinitions,一路下去,直到XmlBeanDefinitionReader的loadBeanDefinitions,也就是第三篇的核心内容。这条线,作用就是解析配置文件,把符合条件的beanDefinition注册到factory中。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
this.refreshBeanFactory();
return this.getBeanFactory();
}
在refreshBeanFactory();中,先判断有没有beanfactory,有的话,先去除引用。
if(this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
创建一个DefaultListableBeanFactory实例,如果有parent,则把parent的bf也传递过来。
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
给bf一个id,采用的系统默认hashcode实现,所以对于这个对象基本上不会重复。然后会把bf放到一个static的map中,使用弱引用包裹。
beanFactory.setSerializationId(this.getId());
loadBeanDefinitions,这是关键方法,在其他篇中详解
至此,refreshBeanFactory解析完毕,getBefactory获得的也是这个factory。