IoC容器初始化是一块非常重要、也是非常难的内容。我会分开几篇介绍。
IoC容器的初始化包括 BeanDefinition 的 Resource 定位、载入和注册三个基本过程。我们以 ApplicationContext 为例,这个类是我们最常见也是最常用的。
主要类关系如下:
ApplicationContext 运行上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于bean的查找可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的Spring应用提供了一个共享的bean定义环境。
主要有三种方式可以使用容器: ClassPathXmlApplicationContext 、 FileSystemXmlApplicationContext 、 AnnotactionConfigApplicationContext 、 XmlWebApplicationContext ,其中第三种是最常用的,也是使用最广泛、最简单的。对于3.0之前更多是使用的xml配置方式,但是现在使用最多的是基于注解的方式,特别是springboot,全部都是基于注解做到零配置。 XmlWebApplicationContext 基本都是在SpringMVC的项目中使用。
bean定义资源准备(设置xml配置文件路径、设置资源加载器)
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
public FileSystemXmlApplicationContext() {
}
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, null);
}
public FileSystemXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 创建资源加载器
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
// 这个方法会在解析xml的子类AbstractBeanDefinitionReader中回调
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
这个类提供了非常多的扩展,可以使用不同类型的构造函数来创建容器。但是都最终会调用到这个构造方法。
public FileSystemXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
这个构造方法在执行的时候首先执行父类 AbstractXmlApplicationContext 的构造方法,父类构造方法会先设置bean资源加载器。然后再调用父类的 setConfigLocations(configLoacations) 方法,这个方法用于设置bean定义资源文件(xml文件)的路径。通过类层架追踪,xml资源的加载最终是在 FileSystemXmlApplicationContext 的父类再网上几层的父类 AbstractXmlApplicationContext 中实现的。
AbstractApplicationContext 的资源加载过程如下:
// FileSystemXmlApplicationContext不断的通过super()方法找到父类的父类的父类。。。
// 最终到达它真正的实现地方
public AbstractApplicationContext() {
// 获取资源处理器(读取spring配置文件)
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
// 因为AbstractApplicationContext继承了DefaultResourceLoader
// DefaultResourceLoader实现了ResourceLoader接口,所以可以直接传this进去
return new PathMatchingResourcePatternResolver(this);
}
资源加载器初始化完成之后,开始设置资源路径。 setConfigLocations(configLocations); 这个方法也是调用父类 AbstractRefreshableConfigApplicationContext 的方法完成的。
public void setConfigLocations(@Nullable String... locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
这个方法执行完毕之后,我们需要加载的bean资源文件都转成一个 String 数组,保存到变量 configLocations 中。
资源加载器准备好之后就是开始加载解析bean配置,然后根据bean定义执行各种操作,包括定义解析、bean初始化等等。
加载、解析、转换XML定义,实例化bean
Spring IoC容器对bean定义的加载是从 refresh() 方法开始的,这个方法是一个模板方法,它的作用是:在创建IoC容器前,如果已经有容器则把容器销毁关闭,然后再重新创建一个新的容器,然后再对容器做初始化。 FileSystemXmlApplicationContext 通过调用父类的 AbstractApplicationContext.refresh() 方法实现容器的创建,加载,初始等一系列操作。回到这个类的构造方法,可以看到在构造方法内部调用了 refresh() 方法。
public FileSystemXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 通过类层级,一直通过super()方法一层层往上找父类的父类的父类。。。。
// 最终找到AbstractApplicationContext的构造方法,
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
通过类层级往上找,他的父类 AbstractXmlApplicationContext 、 AbstractRefreshableConfigApplicationContext 、 AbstractRefreshableApplicationContext 都没有重写 refresh() 方法,这个方法的实现是在 AbstractApplicationContet 中实现的。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 准备此上下文以进行刷新。
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 获取beanFactory实例
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 准备上下文使用的BeanFactory
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 允许子类处理BeanFactory后置处理器
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
// 转到ConfigurationClassPostProcessor里面执行
// 执行bean工厂的后置处理器,里面会有非常多的,非常复杂的BeanFactoryPostProcessor实现类的执行。
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
// 转到PostProcessorRegistrationDelegate
// 注册bean工厂的后置处理器(仅仅是把Spring内部定义的和用户定义的BeanPostProcessor注册到容器中,后面的步骤才会真正执行),
// 这些后置处理器在bean的构造方法执行之后,在执行init方法前后执行指定的逻辑
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化上下文的消息资源,比如message-xxx.properties
initMessageSource();
// Initialize event multicaster for this context.
// 初始化上下文多波器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 在上下文中初始化其他的bean,子类实现
onRefresh();
// Check for listener beans and register them.
// 检查并注册监听器
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 初始化剩下其他的单例bean,包括FactoryBean,(用户定义的大部分类都是在这里实例化的)
// 非常非常重要的方法,是整个IOC容器核心部分
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 初始化上下文生命周期处理器,发布事件
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.
destroyBeans();
// 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();
}
}
}
refresh() 方法的主要作用为了IoC容器bean的生命周期管理提供条件,Spring IoC容器载入bean定义资源文件从其子类 refreshBeanFactory() 方法启动。在创建IoC容器前,如果已经存在则把已经创建的容器销毁关闭,以保障 refresh() 之后是新建的、单例的容器。
这个方法就是Spring最最最核心的实现,整个Spring最核心的特性也是在这个方法实现的,包括后置处理器、事件发布、bean加载、 @Component @Autowired 等等注解、AOP等,下面逐个方法解析学习。