Spring 4.3 源码分析之 IOC 高级容器启动
1. IOC 高级容器启动之猜测
前篇叙述了基本IOC容器的启动, 这里针对的是高级IOC容器(ApplicationContext), 而 高级IOC容器 ApplicationContext中往往有个基本容器 DefaultListableBeanFactory(比如 AbstractRefreshableApplicationContext中的 beanFactory属性), 把高级容器与基本容器的区别主要归结如如下:
1. 继承 DefaultResourceLoader, 本身就拥有 Resource 读取的能力(PS: 但一般都是使用 PathMatchingResourcePatternResolver, 这个类支持 ant 表达式匹配配置文件)
2. 支持多种文件的获取配置文件 classPath, fileSystem, URL 等
3. 统一注册 BeanFactoryPostProcessor, 以及统一进行激活
4. 统一获取 BeanPostProcessor, 并进行注册
5. 容器事件的监听器的添加 ApplicationContextListener
6. 通过 DefaultListableBeanFactory.preInstantiateSingletons 来进行事先创建 Bean (DefaultListableBeanFactory默认是使用时才创建)
2. IOC 高级容器使用 Demo
这里我们还是用和前一篇相似的demo来进行叙述:
<!-- XML 内容 org/springframework/beans/factory/xml/application.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean id="testBean" class="org.springframework.tests.sample.beans.TestBean" />
</beans>
// 测试代码
public void testSingleConfigLocation() {
String resourceName = "org/springframework/beans/factory/xml/application.xml";
// 创建工厂
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(resourceName);
// 获取对应的类 testBean
TestBean bean = (TestBean) factory.getBean("testBean");
assertNotNull(bean);
ctx.close();
}
和上篇不同的一点, 是这里的主角换成了 ClassPathXmlApplicationContext
3. IOC 高级容器启动之构造函数
在这个函数中主要完成了:
1. 设置资源匹配及获取器 PathMatchingResourcePatternResolver
2. 设置配置文件的地址
3. 调用 refresh() 方法来初始化整个容器
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
// 在父类中设置 自己的 ResourcePatternResolver = PathMatchingResourcePatternResolver
super(parent);
// 解析 Bean 定义资源文件的路径, 处理多个资源文件字符串数组
setConfigLocations(configLocations);
// refresh 默认 true
if (refresh) { // refresh()才是正真的主逻辑
refresh();
}
}
4. IOC 高级容器核心方法 refresh() 概述
毫不客气的说 refresh() 是 AbstractApplicationContext 里面的核心中的核心, 主要分成下面几步:
1. prepareRefresh: 准备刷新上下文环境 -> 调用的是 AbstractApplicationContext#refresh -> 设置容器启动的时间 -> 设置容器的激活状态 -> 将 servletContext, servletConfig 设置到 AbstractBeanFactory.StandardEnvironment.PropertySourcesPropertyResolver.propertySources 里面
2. obtainFreshBeanFactory: 调用子类去创建 DefaultListableBeanFactory, 并且去加载配置文件中配置的 Bean 信息
3. prepareBeanFactory: 为 BeanFactory 配置容器特性, 例如类加载器, 标准的 bean 表达式解析器, 属性编辑注册器, 时间处理器等
4. postProcessBeanFactory: 主要是注册 request/session 类型的 scope, 与 ServletContextAwareProcessor
5. invokeBeanFactoryPostProcessors: 实例化并激活所有的 注册到容器中的 BeanFactoryPostProcessor (这里分为针对实现接口 PriorityOrdered, Ordered, 与剩下的其他BeanFactoryPostProcessor分别处理.)
6. registerBeanPostProcessors: 注册 Bean 的后主处理器, 这些后置处理器在 Bean 的创建过程中调用 (PS: getBean中)
7. initMessageSource: 初始化 MessageSource(主要是完成 国际化处理), 如果 beanFactory 不存在此 bean 则采用默认的配置并社会父类 messageSource
8. initApplicationEventMulticaster: 初始化 ApplicationEventMulticaster 事件, 默认使用 SimpleApplicationEventMulticaster 事件
9. onRefresh: 调用子类的某些特殊 Bean 初始化方法, 比如这里的 ThemeSource
10. registerListeners: 在所有 Bean 中查找 ApplicationListener bean, 注册到消息广播器(SimpleApplicationEventMulticaster)中
11. finishBeanFactoryInitialization: 初始化所有除 非单例/抽象类/lazyInit = true 的类; 这里调用 的是 BeanFactory 的 preInstantiateSingletons 这个方法是由 DefaultListableBeanFactory 实现的
12. finishRefresh: 完成刷新过程, 通知生命周期处理器, lifecycleProcessor 刷新过程, 同时发出 ContextRefreshEvent 通知别人
代码部分如下:
/**
* 参考资料:
* http://www.cnblogs.com/ITtangtang/p/3978349.html
*/
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备刷新上下文环境 -> 调用的是 AbstractApplicationContext#refresh -> 设置容器启动的时间 -> 设置容器的激活状态 -> 将 servletContext, servletConfig 设置到 AbstractBeanFactory.StandardEnvironment.PropertySourcesPropertyResolver.propertySources 里面
// Prepare this context for refreshing.
prepareRefresh();
// 调用子类去创建 DefaultListableBeanFactory, 并且去加载配置文件中配置的 Bean 信息
// 调用的是 AbstractRefreshableApplicationContext#refreshBeanFactory -> AbstractRefreshableApplicationContext#getBeanFactory
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 为 BeanFactory 配置容器特性, 例如类加载器, 标准的 bean 表达式解析器, 属性编辑注册器, 时间处理器等
// Prepare the bean factory for use in this context.
// 调用的是 AbstractApplicationContext#prepareBeanFactory
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 主要是注册 request/session 类型的 scope, 与 ServletContextAwareProcessor
// 调用的是 AbstractRefreshableWebApplicationContext#postProcessBeanFactory
postProcessBeanFactory(beanFactory);
// 实例化并激活所有的 注册到容器中的 BeanFactoryPostProcessor (这里分为针对实现接口 PriorityOrdered, Ordered, 与剩下的其他BeanFactoryPostProcessor分别处理.)
// Invoke factory processors registered as beans in the context.
// 其实调用 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors (PS: 里面步骤很多, 但不复杂)
invokeBeanFactoryPostProcessors(beanFactory);
// 注册 Bean 的后主处理器, 这些后置处理器在 Bean 的创建过程中调用 (PS: getBean中)
// 为 BeanFactory 注册 BeanPost 事件处理器
// BeanPostProcessor 是 Bean 后置处理器, 用于监听容器触发的事件
// Register bean processors that intercept bean creation.
// 调用的是 PostProcessorRegistrationDelegate.registerBeanPostProcessors
registerBeanPostProcessors(beanFactory);
// 初始化 MessageSource(主要是完成 国际化处理), 如果 beanFactory 不存在此 bean 则采用默认的配置并社会父类 messageSource
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
// 初始化 ApplicationEventMulticaster 事件, 默认使用 SimpleApplicationEventMulticaster 事件
initApplicationEventMulticaster();
// 调用子类的某些特殊 Bean 初始化方法, 比如这里的 ThemeSource
// Initialize other special beans in specific context subclasses.
// 其实调用的是 AbstractRefreshableWebApplicationContext.onRefresh
onRefresh();
// 在所有 Bean 中查找 ApplicationListener bean, 注册到消息广播器(SimpleApplicationEventMulticaster)中
// Check for listener beans and register them.
// 其实调用的是 AbstractApplicationContext#registerListeners
registerListeners();
// 初始化所有除 非单例/抽象类/lazyInit = true 的类
// 这里调用 的是 BeanFactory 的 preInstantiateSingletons 这个方法是由 DefaultListableBeanFactory 实现的
// Instantiate all remaining (non-lazy-init) singletons.
// 调用的是 AbstractApplicationContext#finishBeanFactoryInitialization
finishBeanFactoryInitialization(beanFactory);
// 完成刷新过程, 通知生命周期处理器, lifecycleProcessor 刷新过程, 同时发出 ContextRefreshEvent 通知别人
// Last step: publish corresponding event.
// 调用的是 AbstractApplicationContext#finishRefresh
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
// 销毁已经创建的 Bean
destroyBeans();
// 取消 refresh 事件, 重置容器的同步标识
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
上面代码中展现了 AbstractApplicationContext 的创建主逻辑, 而关于bean创建那部分在前一篇里面有详细概述
4. IOC 高级容器分类
在使用 applicationContext 中, 主要使用如下几个类型:
1. ClassPathXmlApplicationContext: 通过在构造函数中传入 classpath 下的配置文件地址来生成 ApplicationContext (PS: 这里是通过DefaultResourceLoader来进行获取配置文件)
2. FileSystemXmlApplicationContext: 通过在构造函数中传入配置文件地址来生成 ApplicationContext (PS: 这里是通过PathMatchingResourcePatternResolver 来进行获取配置文件 <- 主要是通过 Ant 表达式获取所有 jar/war 包下满足条件的配置文件)
3. XmlWebApplicationContext: 当在web.xml 里面配置 ContextLoaderListener 时会创建这个 applicationContext(PS: 配置DispatcherServlet时, 也会在构造函数中创建 XmlWebApplicationContext对象)
4. AnnotationConfigWebApplicationContext: 基于一个配置Bean的基于注解的ApplicationContext
5. 总结:
本篇主要结构了 ApplicationContext 的整个启动过程 + 分类, 其实正真要理解 IOC 容器的话, 还是需要看看 Spring 4.3.x IOC 基本容器启动 这篇!
6. 参考资料
Spring技术内幕分析
Spring IOC 原理
Spring 5 源码分析
开涛的 Spring杂谈
伤神的 Spring源码分析
Spring Core
Spring IOC 源码分析
Spring源码情操陶冶
Spring 揭秘 (PS: 这本书绝对给力)
Spring 技术内幕
Spring 源码深度分析
Spring 高级程序设计 (PS:这本书已经绝版, 所以当时是自己下载 pdf, 然后联系淘宝卖家忙帮复印, 其实就是英文版本的 "Pro Spring 3")
深入分析 Java Web(PS: 许令波写的, 虽然是本老书, 但是里面分析得确实很深入)
expert one-on-one J2EE Development without EJB (PS: Spring 作者自己写的书, 当时也是下载 PDF, 联系淘宝卖家复印购买到的)