Spring 对于Java 开发来讲,以及算得上很是基础而且核心的框架了,在有必定开发经验后,阅读源码能更好的提升咱们的编码能力而且让咱们对其更加理解。俗话说知己知彼,百战不殆。当你对Spring 掌握熟透以后,也就没什么能过阻拦你在开发路上前进了。html
IOC 整体来讲有两处地方最重要,一个是建立Bean容器,一个是初始化。在本文中,主要为你们讲解了 IOC Bean 容器建立过程。后续将会补上初始化部分的知识。java
为了保持文章的严谨性,若是读者发现我哪里说错了请必定不吝指出,很是但愿能够听到读者的声音。同时能过纠正本身的误解。spring
本文主要采用了ClassPathXmlApplication做为Spring IOC 容器,从 ClassPathXmlApplication 分析容器建立的过程,首先来看一下ClassPathXmlApplication 的依赖关系: 数组
首先咱们先来观察一下核心主干:bash
咱们能够经过对ClassPathXmlApplicationContext 的父类名称,了解其主要功能:app
经过上述主要类的分析,相信你们对Spring IOC 的初始化有了大概的认识。框架
简单来讲,Spring IOC 容器建立的具体流程以下:ide
咱们将图片对应到类的调用,具体步骤为:源码分析
一、ClassPathXmlApplicationContext -> 经过构造器,读取XML配置文件地址信息ui
二、AbstractApplicationContext -> refresh() 初始化容器
三、AbstractRefreshableApplicationContext -> BeanFactory 初始化
四、AbstractXmlApplicationContext -> loadBeanDifinition 读取资源信息(加载Bean)
五、XmlBeanDefinitionReader -> 将资源文件解析成 BeanDefinition
六、DefaultBeanDefinitionDocumentReader -> 将BeanDefinition 注册到BeanFactory 中
因为Spring IOC部分源码主要包括了:容器建立以及Bean 初始化两部分。在本文内容中,主要介绍一下,容器建立的过程!
ApplicationContext.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.xsd">
<bean id="User" class="com.charles.business.model.User">
<property name="username" value="jaycekon"/>
<property name="phone" value="1881412***"/>
</bean>
</beans>
复制代码
代码启动入口:
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
User user = (User) applicationContext.getBean("User");
Assert.notNull(user, "容器初始化异常!");
logger.info("初始化结果:{}", JSONObject.toJSONString(user));
}
复制代码
接下来咱们根据ClassPathXmlApplication 的依赖图进行逐一分析每个过程。
核心方法:
org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String[], boolean, org.springframework.context.ApplicationContext)
复制代码
从上图能够看出,ClassPathXmlApplicationContext 类中并无提供什么特别的方法,除构造器外,只有一个获取Resource 的方法(与之类似的 AbstractRefreshableConfigApplicationContext 中提供类获取资源定位的方法),主要用于保存资源信息。
ClassPathXmlApplicationContext 中的主要构造方法:
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 调用父类构造方法
super(parent);
// AbstractRefreshableConfigApplicationContext 具体实现,保存资源定位信息
setConfigLocations(configLocations);
if (refresh) {
// AbstractApplicationContext 具体实现,初始化容器与Bean
refresh();
}
}
复制代码
能够看到,ClassPathXmlApplicationContext 这个类至关简单,主要做用:
核心方法:
org.springframework.context.support.AbstractApplicationContext#refresh
复制代码
AbstractApplicationContext 是Spring IOC 容器中核心类,对父类,接口提供了具体实现,其中的方法很是丰富,在这里咱们主要介绍其中的核心方法 refresh()。 这里简单说下为何是 refresh(),而不是 init() 这种名字的方法。由于 ApplicationContext 创建起来之后,其实咱们是能够经过调用 refresh() 这个方法重建的,这样会将原来的 ApplicationContext 销毁,而后再从新执行一次初始化操做。
public void refresh() throws BeansException, IllegalStateException {
//对容器初始化进行加锁操做,比免在建立的同时,重复操做。
synchronized (this.startupShutdownMonitor) {
// 设置容器初始化世界,表示容器如今isActive,初始化上下文环境中的任何占位符属性源
prepareRefresh();
// 核心步骤,主要功能是:让子类进行BeanFactory 初始化,而且将Bean信息 转换为BeanFinition,最后注册到容器中
// 固然,这里说的 Bean 尚未初始化,只是配置信息都提取出来了
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
prepareBeanFactory(beanFactory);
try {
// 后续步骤将在下次进行分析,本文主要核心为上面几个步骤
...
}
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(); } } } 复制代码
从AbstractApplicationContext 中的refresh()咱们能够粗略的领会到,Spring IOC 的核心流程,基本在这里完成的,从容器建立,资源解析,Bean建立等一系列步骤。都是由这个方法进行控制。
在本文中,咱们主要关心的一个点就是:
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
复制代码
经过上图,咱们能够直观的了解到ApplicationContext 与BeanFactory 之间的关系。这里建立的 ConfigurableListableBeanFactory 实际包含了BeanFactory 三个分支的绝大部分功能。
一、ApplicationContext 继承了 ListableBeanFactory,这个 Listable 的意思就是,经过这个接口,咱们能够获取多个 Bean,最顶层 BeanFactory 接口的方法都是获取单个 Bean 的。
二、ApplicationContext 继承了 HierarchicalBeanFactory,Hierarchical 单词自己已经能说明问题了,也就是说咱们能够在应用中起多个 BeanFactory,而后能够将各个 BeanFactory 设置为父子关系。
三、AutowireCapableBeanFactory 这个名字中的 Autowire 你们都很是熟悉,它就是用来自动装配 Bean 用的,可是仔细看上图,ApplicationContext 并无继承它,不过不用担忧,不使用继承,不表明不可使用组合,若是你看到 ApplicationContext 接口定义中的最后一个方法 getAutowireCapableBeanFactory() 就知道了。
四、ConfigurableListableBeanFactory 也是一个特殊的接口,看图,特殊之处在于它继承了第二层全部的三个接口,而 ApplicationContext 没有。这点以后会用到。
咱们来看一下 obtainFreshBeanFactory() 方法的主要内容:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//AbstractRefreshableApplicationContext 中实现,主要用于刷新BeanFactory,加载 Bean 定义、注册 Bean 等等
refreshBeanFactory();
//获取上述步骤中 建立好的 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
复制代码
核心方法:
org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
复制代码
其实在阅读源码的过程当中,读懂类的命名能过为咱们提供很大的帮助,例如 AbstractRefreshableApplicationContext 这个类,能够看出她继承了AbstractApplicationContext 而且主要实现了其中的refresh 功能,固然,这里的refresh 并非指 refresh() 方法。而是指的:refreshBeanFactory()
@Override
protected final void refreshBeanFactory() throws BeansException {
//判断当前ApplicationContext是否已经有 BeanFactory ,若是有,销毁全部 Bean,关闭 BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 初始化一个 DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 设置 BeanFactory 的两个配置属性:是否容许 Bean 覆盖、是否容许循环引用
customizeBeanFactory(beanFactory);
// 读取配置文件信息,加载 Bean 到 BeanFactory 中
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
复制代码
可能你们以前会以前会认为ApplicationContext 与 BeanFactory 之间是继承关系,可是在实际应用中,可是它不该该被理解为 BeanFactory 的实现类,ApplicationContext 是持有 BeanFactory 的一个实例,而且之后全部的 BeanFactory相关的操做实际上是给这个实例来处理的。
简单介绍一下 customizeBeanFactory 方法:
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}
复制代码
核心方法:
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)
复制代码
经历了那么长的路程,才终于到了XML -> Resource -> BeanDefinition 这个步骤:
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//给这个 BeanFactory 实例化一个 XmlBeanDefinitionReader BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 设置默认环境配置
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化 BeanDefinitionReader
initBeanDefinitionReader(beanDefinitionReader);
// 将配置文件读取,解析成BeanDefinition
loadBeanDefinitions(beanDefinitionReader);
}
复制代码
这个方法比较简单,咱们直接往下走,进入到loadBeanDefinitions()方法中:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
复制代码
能够看到,上面主要分红了两个步骤,一个是经过Resource 进行解析,一个是经过configLocations 进行解析(这个步骤,主要多了将资源定位符抓换为Resource 的过程)。
后续的Bean 解析,将会在下述方法进行解析:
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)
复制代码
因为篇幅缘由,本文主要介绍了Spring IOC 容器建立的源码过程,关于Bean 解析,以及后续步骤的源码分析,将会在后续补上。最后咱们来回顾一下主流程:
参考资料: