作为一个Java程序员,大家都知道,如果一个项目集成了Spring,那么,项目中必然会有一个applicationContext.xml,那么这个applicationContext.xml是干什么的呢?你会回答说,写一些需要的配置。
如果你能听懂这些,接下来的内容我们还能聊得下去。
作为一个Web项目,首先我们肯定会有一个web.xml,这个web.xml是做什么的呢?是用于容器解析并配置上下文环境的,比如说Tomcat用Host容器解析web.xml,我们来看一下集成Spring部分的代码。
<!-- 加载spring的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml;
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</context-param>
很常见的样子,但是我们经常对此一笑而过,不留半分思考,那么今天我们就来看看ContextLoaderListener类:
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
你可能说,我有点头绪了,这个contextInitialized可能要搞事啊。的确是,容器启动后,会调用ContextLoaderListener#contextInitialized(),来进行相关操作。但是,并不是说,我们随便写一个类,放到web.xml,容器就会调用,仔细看,这个类是重写的,是来自ServletContextListener接口,这也就说,如果我们想写一个web项目启动时的监听器,首先需要实现ServletContextListener接口。
我们接着看contextInitialized()方法,调用了父类ContextLoader#initWebApplicationContext()方法:
public class ContextLoader {
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
...
if (this.context == null) {
// 创建容器
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// 容器还没有被初始化
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
// 设置父类容器
cwac.setParent(parent);
}
// 配置并初始化容器
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
...
}
}
如果上下文为空,那么我们将创建一个上下文容器。创建好上下文后,便进行容器的配置与初始化,继续走:
public class ContextLoader {
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// 获取ID
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
// 生成ID
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
// Spring容器初始化核心:刷新
wac.refresh();
}
}
领号,给入场券后,我们终于在最后看到了Spring容器初始化的核心:refresh():
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 容器启动之前的准备工作
prepareRefresh();
// 很关键:创建工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 配置工厂
prepareBeanFactory(beanFactory);
try {
// 设置工厂的后处理器
postProcessBeanFactory(beanFactory);
// 调用工厂的后处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注册工厂的后处理器
registerBeanPostProcessors(beanFactory);
// 初始化上下文中的消息源
initMessageSource();
// 初始化应用时间广播器
initApplicationEventMulticaster();
// 默认空逻辑,请自行进行表演
onRefresh();
// 注册应用监听器
registerListeners();
// 很关键:完成工厂的初始化
finishBeanFactoryInitialization(beanFactory);
// 完成初始化,发布刷新事件,监听事件可以进场了
finishRefresh();
}
...
}
}
}
为了增加篇幅文字,我们继续BB一些。
- prepareRefresh()。记录下时间,状态等信息。
- obtainFreshBeanFactory()。核心,很关键,比如我们applicationContext.xml的解析,Bean注册等。
- prepareBeanFactory(beanFactory。各种设置,提供了标准上下文环境元素。
- BeanProcessor。这里有三个关于后置处理的方法,再未来的篇幅中,我们会介绍后置处理的实际应用,到时候你会恍然大悟。
- initMessageSource()。初始化上下文中的消息源,可能你会往什么JMS方向想,但是我感觉你想歪了,目前作者仅是在国际化中使用过MessageSource接口。
- initApplicationEventMulticaster()。初始化上下文中的事件机制,作者认为这是一个异步发布事件的驱动,如果你可以自力更生,Spring容器满足你的需求。如果你不行,那么Spring会自动为你配置一个SimpleApplicationEventMulticaster事件驱动。
- onRefresh()。名曰初始化一些特殊的Bean,其实就是用户自定义的Bean。
- registerListeners()。上面既然有异步发送事件的模型,那么肯定要注册监听者。
- finishBeanFactoryInitialization(beanFactory)。核心,很关键,关键事件getBean()。
- finishRefresh()。容器初始化结束了,广而告之。