这是我参与8月更文挑战的第29天,活动详情查看: 8月更文挑战
本文使用的是4.3.20版本的spring,是5.x之前的比较新的版本。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.20.RELEASE</version>
</dependency>
// 定义一个接口
public interface MessageService {
void sayHello(String name);
}
// 接口实现了类
public class MessageServiceImpl implements MessageService {
@Override
public void sayHello(String name) {
System.out.println(name + "say hello");
}
}
// 容器启动类
public class Main {
public static void main(String[] args) {
final ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"application.xml");
final MessageService messageService = (MessageService) applicationContext
.getBean("messageService");
// 执行
messageService.sayHello("小明同学");
}
}
<!--application.xml 配置文件-->
<bean id="messageService" class="*.MessageServiceImpl" />
以上例子很简单,不过也够引出本文的主题了,就是怎么样通过配置文件来启动 Spring 的 ApplicationContext ?也就是我们今天要分析的 IOC 的核心了。ApplicationContext 启动过程中,会负责创建实例 Bean,往各个 Bean 中注入依赖等。
除了ClassPathXmlApplicationContext ,还有两个类FileSystemXmlApplicationContext 和 AnnotationConfigApplicationContext,FileSystemXmlApplicationContext 的构造函数需要一个 xml 配置文件在系统中的路径,其他和 ClassPathXmlApplicationContext 基本上一样,AnnotationConfigApplicationContext 是基于注解来使用的,它不需要配置文件,采用 java 配置类和各种注解来配置,是比较简单的方式,也是大势所趋吧。 BeanFactory,从名字上也很好理解,生产 bean 的工厂,它负责生产和管理各个 bean 实例。IoC 中最核心的接口是 BeanFactory ,提供 IoC 的高级服务,而 ApplicationContext 是建立在 BeanFactory 基础之上提供抽象的面向应用的服务。
从上图可以看出:
ApplicationContext
继承了ListableBeanFactory
,这个Listable
的意思就是,通过这个接口,我们可以获取多个 Bean,大家看源码会发现,最顶层 BeanFactory 接口的方法都是获取单个 Bean 的。ApplicationContext
继承了HierarchicalBeanFactory
,Hierarchical
单词本身已经能说明问题了,也就是说我们可以在应用中起多个BeanFactory
,然后可以将各个BeanFactory
设置为父子关系。
除了上面两个继承之外,ApplicationContext
还提供了AutowireCapableBeanFactory getAutowireCapableBeanFactory()
方法。
从ClassPathXmlApplicationContext开始
整个链路复杂到可怕,所以想要真正读懂这部分代码需要耐心的一步步debug下去。
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(String[] configLocations,
boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
// 根据提供的路径,处理成配置文件数组(以分号、逗号、空格、tab、换行符分割)
setConfigLocations(configLocations);
if (refresh) {
refresh(); // 核心代码
}
}
接下来,就是 refresh()
,这里简单说下为什么是 refresh()
,而不是 init()
这种名字的方法。因为 ApplicationContext
建立起来以后,其实我们是可以通过调用 refresh()
这个方法重建的,refresh()
会将原来的 ApplicationContext
销毁,然后再重新执行一次初始化操作。
public void refresh() throws BeansException, IllegalStateException {
// 加锁,防止refresh()还未结束,又有新的容器启动或者销毁动作
synchronized (this.startupShutdownMonitor) {
// 准备工作,记录容器的启动时间,标记容器的启动状态为已启动,处理文件中的占位符
prepareRefresh();
/*
这一步比较重要,这步完成后,配置文件就会解析成一个个Bean 定义,注册到 BeanFactory中,
当然这里说的Bean还未完成初始化,只是将配置信息提取出来,并将这些信息保存起来。
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 给beanFactory设置一些属性:环境、系统属性
prepareBeanFactory(beanFactory);
try {
// 【这里需要知道 BeanFactoryPostProcessor 这个知识点,Bean 如果实现了此接口,
// 那么在容器初始化以后,Spring会负责调用里面的 postProcessBeanFactory 方法。】
// 这里是提供给子类的扩展点,到这里的时候,所有的Bean都加载、注册完成了,
// 但是都还没有初始化
postProcessBeanFactory(beanFactory);
// 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
// 此接口两个方法: postProcessBeforeInitialization和postProcessAfterInitialization
// 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
registerBeanPostProcessors(beanFactory);
// 初始化MessageSource组件,负责国际化处理,消息的绑定和解析
initMessageSource();
// 初始化当前 ApplicationContext 的事件广播器
initApplicationEventMulticaster();
// 空实现,钩子方法,用于子类实现特殊bean的初始化
onRefresh();
// 注册事件监听器,监听器需要实现 ApplicationListener 接口
registerListeners();
// 初始化所有的 singleton beans
finishBeanFactoryInitialization(beanFactory);
// 最后,广播事件,ApplicationContext 初始化完成
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();
}
}
}
先整体了解一下IoC容器的启动过程,下面会对
refresh()
方法中重要的部分进行拆解。整个流程还是十分清晰的,但是每一步的代码都埋的十分深,读起来可能有点费劲。