Spring的IOC的底层原理

[TOC]

IOC的理解

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:

  ●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

  ●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

第一个过程是Resource资源定位。这个Resouce指的是BeanDefinition的资源定位。这个过程就是容器找数据的过程,就像水桶装水需要先找到水一样。
第二个过程这个载入过程是把用户定义好的Bean表示成Ioc容器内部的数据结构,而这个容器内部的数据结构就是BeanDefition。

1
2
3
4
5
6
refresh()
这个过程是最繁琐,也是最重要的一个过程。这一个过程分为以下几步,

构造一个BeanFactory,也就是IOC容器
调用XML解析器得到document对象
按照Spring的规则解析BeanDefition

第三个过程是向IOC容器注册这些BeanDefinition的过程,这个过程就是将前面的BeanDefition保存到HashMap中的过程。

Spring中Bean的实例化过程图示
image-20210310001003747

测试的代码

1
2
3
4
5
public class HelloService {
public void hello(){
System.out.println("hello spring");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class HelloServiceTest {
@Test
public void test1() throws Exception {
/**
* 1.加载Spring的配置文件
* 2.取出Bean容器中的实例
* 3.调用bean方法
*/
// 1.加载Spring的配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 2.取出Bean容器中的实例
HelloService helloService = (HelloService) context.getBean("helloService");
// 3.调用bean方法
// helloService.hello();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
<?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">
<!--
xmlns 即 xml namespace xml 使用的命名空间
xmlns:xsi 即 xml schema instance xml 遵守的具体规范
xsi:schemaLocation 本文档 xml 遵守的规范 官方指定
-->
<bean class="com.didi.beanpostprocessor.LoggerBeanPostProcessor"/>
<bean id="helloService" class="com.didi.service.HelloService"></bean>
</beans>
1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.17.RELEASE</version>
</dependency>

BeanPostProcessor

BeanPostProcessor 是 bean 实例化时的后置处理器,包含两个方法,其源码如下:

1
2
3
4
5
6
7
8
public interface BeanPostProcessor {
// bean 初始化前的回调方法
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

// bean 初始化后的回调方法
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

BeanPostProcessor 是 Spring 框架的一个扩展点,通过实现 BeanPostProcessor 接口,我们就可插手 bean 实例化的过程。比如大家熟悉的 AOP 就是在 bean 实例后期间将切面逻辑织入 bean 实例中的,AOP 也正是通过 BeanPostProcessor 和 IOC 容器建立起了联系。这里我来演示一下 BeanPostProcessor 的使用方式,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 日志后置处理器,将会在 bean 创建前、后打印日志
*/
public class LoggerBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Before " + beanName + " Initialization");
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("After " + beanName + " Initialization");
return bean;
}
}

FactoryBean

FactoryBean?看起来是不是很像 BeanFactory 孪生兄弟。不错,他们看起来很像,但是他们是不一样的。FactoryBean 是一种工厂 bean,与普通的 bean 不一样,FactoryBean 是一种可以产生 bean 的 bean,好吧说起来很绕嘴。FactoryBean 是一个接口,我们可以实现这个接口。下面演示一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class HelloFactoryBean implements FactoryBean<Hello> {

@Override
public Hello getObject() throws Exception {
Hello hello = new Hello();
hello.setContent("hello");
return hello;
}

@Override
public Class<?> getObjectType() {
return Hello.class;
}

@Override
public boolean isSingleton() {
return true;
}
}

配置如下:

1
<bean id="helloFactory" class="xyz.coolblog.service.HelloFactoryBean"/>

测试代码如下:

1
2
3
4
5
6
7
8
9
10
public class ApplicationContextTest {

@Test
public void testFactoryBean() {
String configLocation = "application-factory-bean.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
System.out.println("helloFactory -> " + applicationContext.getBean("helloFactory"));
System.out.println("&helloFactory -> " + applicationContext.getBean("&helloFactory"));
}
}

测试结果如下:
img

由测试结果可以看到,当我们调用 getBean(“helloFactory”) 时,ApplicationContext 会返回一个 Hello 对象,该对象是 HelloFactoryBean 的 getObject 方法所创建的。如果我们想获取 HelloFactoryBean 本身,则可以在 helloFactory 前加上一个前缀&,即&helloFactory

GetBean

简单说一下本章的内容安排吧,在本章的开始,也就是2.1节,我将会分析getBean(String)方法整体的实现逻辑。但不会分析它所调用的方法,这些方法将会在后续几节中依次进行分析。那接下来,我们就先来看看 getBean(String) 方法是如何实现的吧。

img

2.1 俯瞰 getBean(String) 源码

在本小节,我们先从战略上俯瞰 getBean(String) 方法的实现源码。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
public Object getBean(String name) throws BeansException {
// getBean 是一个空壳方法,所有的逻辑都封装在 doGetBean 方法中
return doGetBean(name, null, null, false);
}

protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {

/*
* 通过 name 获取 beanName。这里不使用 name 直接作为 beanName 有两点原因:
* 1. name 可能会以 & 字符开头,表明调用者想获取 FactoryBean 本身,而非 FactoryBean
* 实现类所创建的 bean。在 BeanFactory 中,FactoryBean 的实现类和其他的 bean 存储
* 方式是一致的,即 <beanName, bean>,beanName 中是没有 & 这个字符的。所以我们需要
* 将 name 的首字符 & 移除,这样才能从缓存里取到 FactoryBean 实例。
* 2. 若 name 是一个别名,则应将别名转换为具体的实例名,也就是 beanName。
*/
final String beanName = transformedBeanName(name);
Object bean;

/*
* 从缓存中获取单例 bean。Spring 是使用 Map 作为 beanName 和 bean 实例的缓存的,所以这
* 里暂时可以把 getSingleton(beanName) 等价于 beanMap.get(beanName)。当然,实际的
* 逻辑并非如此简单,后面再细说。
*/
Object sharedInstance = getSingleton(beanName);

/*
* 如果 sharedInstance = null,则说明缓存里没有对应的实例,表明这个实例还没创建。
* BeanFactory 并不会在一开始就将所有的单例 bean 实例化好,而是在调用 getBean 获取
* bean 时再实例化,也就是懒加载。
* getBean 方法有很多重载,比如 getBean(String name, Object... args),我们在首次获取
* 某个 bean 时,可以传入用于初始化 bean 的参数数组(args),BeanFactory 会根据这些参数
* 去匹配合适的构造方法构造 bean 实例。当然,如果单例 bean 早已创建好,这里的 args 就没有
* 用了,BeanFactory 不会多次实例化单例 bean。
*/
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}

/*
* 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果
* sharedInstance 是 FactoryBean 类型的,则需调用 getObject 工厂方法获取真正的
* bean 实例。如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回
* 即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已。
*/
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
/*
* 如果上面的条件不满足,则表明 sharedInstance 可能为空,此时 beanName 对应的 bean
* 实例可能还未创建。这里还存在另一种可能,如果当前容器有父容器,beanName 对应的 bean 实例
* 可能是在父容器中被创建了,所以在创建实例前,需要先去父容器里检查一下。
*/
else {
// BeanFactory 不缓存 Prototype 类型的 bean,无法处理该类型 bean 的循环依赖问题
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}

// 如果 sharedInstance = null,则到父容器中查找 bean 实例
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 获取 name 对应的 beanName,如果 name 是以 & 字符开头,则返回 & + beanName
String nameToLookup = originalBeanName(name);
// 根据 args 是否为空,以决定调用父容器哪个方法获取 bean
if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}

if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}

try {
// 合并父 BeanDefinition 与子 BeanDefinition,后面会单独分析这个方法
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);

// 检查是否有 dependsOn 依赖,如果有则先初始化所依赖的 bean
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
/*
* 检测是否存在 depends-on 循环依赖,若存在则抛异常。比如 A 依赖 B,
* B 又依赖 A,他们的配置如下:
* <bean id="beanA" class="BeanA" depends-on="beanB">
* <bean id="beanB" class="BeanB" depends-on="beanA">
*
* beanA 要求 beanB 在其之前被创建,但 beanB 又要求 beanA 先于它
* 创建。这个时候形成了循环,对于 depends-on 循环,Spring 会直接
* 抛出异常
*/
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 注册依赖记录
registerDependentBean(dep, beanName);
try {
// 加载 depends-on 依赖
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}

// 创建 bean 实例
if (mbd.isSingleton()) {
/*
* 这里并没有直接调用 createBean 方法创建 bean 实例,而是通过
* getSingleton(String, ObjectFactory) 方法获取 bean 实例。
* getSingleton(String, ObjectFactory) 方法会在内部调用
* ObjectFactory 的 getObject() 方法创建 bean,并会在创建完成后,
* 将 bean 放入缓存中。关于 getSingleton 方法的分析,本文先不展开,我会在
* 后面的文章中进行分析
*/
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
// 创建 bean 实例
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
// 如果 bean 是 FactoryBean 类型,则调用工厂方法获取真正的 bean 实例。否则直接返回 bean 实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

// 创建 prototype 类型的 bean 实例
else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}

// 创建其他类型的 bean 实例
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}

// 如果需要进行类型转换,则在此处进行转换。类型转换这一块我没细看,就不多说了。
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}

// 返回 bean
return (T) bean;
}

以上就是getBean(String)doGetBean(String, Class, Object[], boolean)两个方法的分析。代码很长,需要一点耐心阅读。为了凸显方法的主逻辑,大家可以对代码进行一定的删减,删除一些日志和异常代码,也可以删除一些不是很重要的逻辑。另外由于 doGetBean 方法调用了其他的很多方法,在看代码时,经常会忘掉 doGetBean 所调用的方法是怎么实现的。比如 getSingleton 方法出现了两次,但两个方法并不同,在看第二个的 getSingleton 方法时,可能会忘掉第一个 getSingleton 是怎么实现的。另外,如果你想对比两个重载方法的异同,在 IDEA 里跳来跳去也是很不方便。为此,我使用了 sublime 进行分屏,左屏是删减后的 doGetBean 方法,右屏是 doGetBean 调用的一些方法,这样看起来会方便一点。忘了某个方法的实现逻辑后,可以到右屏查看,也可进行对比。分屏效果如下:

img

这里我为了演示,删除了不少东西。大家可以按需进行删减,并配上注释,辅助理解。

看完了源码,下面我来简单总结一下 doGetBean 的执行流程。如下:

1.转换 beanName

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1. <beanName, bean>,beanName 中是没有 & 这个字符的。所以我们需要
while循环处理别名
* 将 name 的首字符 & 移除,这样才能从缓存里取到 FactoryBean 实例。
2.* 这里使用 while 循环进行处理,

原因是:可能会存在多重别名的问题,即别名指向别名。比如下面
* 的配置:
* <bean id="hello" class="service.Hello"/>
* <alias name="hello" alias="aliasA"/>
* <alias name="aliasA" alias="aliasB"/>
*
* 上面的别名指向关系为 aliasB -> aliasA -> hello,对于上面的别名配置,aliasMap 中数据
* 视图为:aliasMap = [<aliasB, aliasA>, <aliasA, hello>]。通过下面的循环解析别名
* aliasB 最终指向的 beanName

2.从缓存中获取实例

1
对于单例 bean,Spring 容器只会实例化一次。后续再次获取时,只需直接从缓存里获取即可,无需且不能再次实例化(否则单例就没意义了)

4.若上面的条件不成立,则到父容器中查找 beanName 对有的 bean 实例,存在则直接返回

5.若父容器中不存在,则进行下一步操作 – 合并 BeanDefinition

1
Spring 支持配置继承,在 标签中可以使用`parent`属性配置父类 bean。这样子类 bean 可以继承父类 bean 的配置信息,同时也可覆盖父类中的配置。比如下面的配置:

6.处理 depends-on 依赖

7.创建并缓存 bean

8.调用 getObjectForBeanInstance 方法,并按 name 规则返回相应的 bean 实例

9.按需转换 bean 类型,并返回转换后的 bean 实例。

以上步骤对应的流程图如下:

img

2.2 beanName 转换

在获取 bean 实例之前,Spring 第一件要做的事情是对参数 name 进行转换。转换的目的主要是为了解决两个问题,第一个是处理以字符 & 开头的 name,防止 BeanFactory 无法找到与 name 对应的 bean 实例。第二个是处理别名问题,Spring 不会存储 <别名, bean 实例> 这种映射,仅会存储 <beanName, bean>。所以,同样是为了避免 BeanFactory 找不到 name 对应的 bean 的实例,对于别名也要进行转换。接下来,我们来简单分析一下转换的过程,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
protected String transformedBeanName(String name) {
// 这里调用了两个方法:BeanFactoryUtils.transformedBeanName(name) 和 canonicalName
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

/** 该方法用于处理 & 字符 */
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
String beanName = name;
// 循环处理 & 字符。比如 name = "&&&&&helloService",最终会被转成 helloService
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
return beanName;
}

/** 该方法用于转换别名 */
public String canonicalName(String name) {
String canonicalName = name;
String resolvedName;
/*
* 这里使用 while 循环进行处理,原因是:可能会存在多重别名的问题,即别名指向别名。比如下面
* 的配置:
* <bean id="hello" class="service.Hello"/>
* <alias name="hello" alias="aliasA"/>
* <alias name="aliasA" alias="aliasB"/>
*
* 上面的别名指向关系为 aliasB -> aliasA -> hello,对于上面的别名配置,aliasMap 中数据
* 视图为:aliasMap = [<aliasB, aliasA>, <aliasA, hello>]。通过下面的循环解析别名
* aliasB 最终指向的 beanName
*/
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}

2.3 从缓存中获取 bean 实例

对于单例 bean,Spring 容器只会实例化一次。后续再次获取时,只需直接从缓存里获取即可,无需且不能再次实例化(否则单例就没意义了)。从缓存中取 bean 实例的方法是getSingleton(String),下面我们就来看看这个方法实现方式吧。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}

/**
* 这里解释一下 allowEarlyReference 参数,allowEarlyReference 表示是否允许其他 bean 引用
* 正在创建中的 bean,用于处理循环引用的问题。关于循环引用,这里先简单介绍一下。先看下面的配置:
*
* <bean id="hello" class="xyz.coolblog.service.Hello">
* <property name="world" ref="world"/>
* </bean>
* <bean id="world" class="xyz.coolblog.service.World">
* <property name="hello" ref="hello"/>
* </bean>
*
* 如上所示,hello 依赖 world,world 又依赖于 hello,他们之间形成了循环依赖。Spring 在构建
* hello 这个 bean 时,会检测到它依赖于 world,于是先去实例化 world。实例化 world 时,发现
* world 依赖 hello。这个时候容器又要去初始化 hello。由于 hello 已经在初始化进程中了,为了让
* world 能完成初始化,这里先让 world 引用正在初始化中的 hello。world 初始化完成后,hello
* 就可引用到 world 实例,这样 hello 也就能完成初始了。关于循环依赖,我后面会专门写一篇文章讲
* 解,这里先说这么多。
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从 singletonObjects 获取实例,singletonObjects 中缓存的实例都是完全实例化好的 bean,可以直接使用
Object singletonObject = this.singletonObjects.get(beanName);
/*
* 如果 singletonObject = null,表明还没创建,或者还没完全创建好。
* 这里判断 beanName 对应的 bean 是否正在创建中
*/
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从 earlySingletonObjects 中获取提前曝光的 bean,用于处理循环引用
singletonObject = this.earlySingletonObjects.get(beanName);
// 如果如果 singletonObject = null,且允许提前曝光 bean 实例,则从相应的 ObjectFactory 获取一个原始的(raw)bean(尚未填充属性)
if (singletonObject == null && allowEarlyReference) {
// 获取相应的工厂类
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 提前曝光 bean 实例,用于解决循环依赖
singletonObject = singletonFactory.getObject();
// 放入缓存中,如果还有其他 bean 依赖当前 bean,其他 bean 可以直接从 earlySingletonObjects 取结果
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

上面的代码虽然不长,但是涉及到了好几个缓存集合。如果不知道这些缓存的用途是什么,上面源码可能就很难弄懂了。这几个缓存集合用的很频繁,在后面的代码中还会出现,所以这里介绍一下。如下:

缓存 用途
singletonObjects 用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
earlySingletonObjects 用于存放还在初始化中的 bean,用于解决循环依赖
singletonFactories 用于存放 bean 工厂。bean 工厂所产生的 bean 是还未完成初始化的 bean。如代码所示,bean 工厂所生成的对象最终会被缓存到 earlySingletonObjects 中

关于 getSingleton 先说到这里,getSingleton 源码并不多。但涉及到了循环依赖的相关逻辑,如果对这一块不理解可能不知道代码所云。等后面分析循环依赖的时候,我会再次分析这个方法,所以暂时不理解也没关系。

2.4 合并父 BeanDefinition 与子 BeanDefinition

Spring 支持配置继承,在 标签中可以使用parent属性配置父类 bean。这样子类 bean 可以继承父类 bean 的配置信息,同时也可覆盖父类中的配置。比如下面的配置:

1
2
3
4
5
6
7
<bean id="hello" class="xyz.coolblog.innerbean.Hello">
<property name="content" value="hello"/>
</bean>

<bean id="hello-child" parent="hello">
<property name="content" value="I`m hello-child"/>
</bean>

如上所示,hello-child 配置继承自 hello。hello-child 未配置 class 属性,这里我们让它继承父配置中的 class 属性。然后我们写点代码测试一下,如下:

1
2
3
4
String configLocation = "application-parent-bean.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
System.out.println("hello -> " + applicationContext.getBean("hello"));
System.out.println("hello-child -> " + applicationContext.getBean("hello-child"));

测试结果如下:
img

由测试结果可以看出,hello-child 在未配置 class 的属性下也实例化成功了,表明它成功继承了父配置的 class 属性。

看完代码演示,接下来我们来看看源码吧。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// 检查缓存中是否存在“已合并的 BeanDefinition”,若有直接返回即可
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if (mbd != null) {
return mbd;
}
// 调用重载方法
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
throws BeanDefinitionStoreException {
// 继续调用重载方法
return getMergedBeanDefinition(beanName, bd, null);
}

protected RootBeanDefinition getMergedBeanDefinition(
String beanName, BeanDefinition bd, BeanDefinition containingBd)
throws BeanDefinitionStoreException {

synchronized (this.mergedBeanDefinitions) {
RootBeanDefinition mbd = null;

// 我暂时还没去详细了解 containingBd 的用途,尽管从方法的注释上可以知道 containingBd 的大致用途,但没经过详细分析,就不多说了。见谅
if (containingBd == null) {
mbd = this.mergedBeanDefinitions.get(beanName);
}

if (mbd == null) {
// bd.getParentName() == null,表明无父配置,这时直接将当前的 BeanDefinition 升级为 RootBeanDefinition
if (bd.getParentName() == null) {
if (bd instanceof RootBeanDefinition) {
mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
}
else {
mbd = new RootBeanDefinition(bd);
}
}
else {
BeanDefinition pbd;
try {
String parentBeanName = transformedBeanName(bd.getParentName());
/*
* 判断父类 beanName 与子类 beanName 名称是否相同。若相同,则父类 bean 一定
* 在父容器中。原因也很简单,容器底层是用 Map 缓存 <beanName, bean> 键值对
* 的。同一个容器下,使用同一个 beanName 映射两个 bean 实例显然是不合适的。
* 有的朋友可能会觉得可以这样存储:<beanName, [bean1, bean2]> ,似乎解决了
* 一对多的问题。但是也有问题,调用 getName(beanName) 时,到底返回哪个 bean
* 实例好呢?
*/
if (!beanName.equals(parentBeanName)) {
/*
* 这里再次调用 getMergedBeanDefinition,只不过参数值变为了
* parentBeanName,用于合并父 BeanDefinition 和爷爷辈的
* BeanDefinition。如果爷爷辈的 BeanDefinition 仍有父
* BeanDefinition,则继续合并
*/
pbd = getMergedBeanDefinition(parentBeanName);
}
else {
// 获取父容器,并判断,父容器的类型,若不是 ConfigurableBeanFactory 则判抛出异常
BeanFactory parent = getParentBeanFactory();
if (parent instanceof ConfigurableBeanFactory) {
pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
}
else {
throw new NoSuchBeanDefinitionException(parentBeanName,
"Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
"': cannot be resolved without an AbstractBeanFactory parent");
}
}
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
"Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
}
// 以父 BeanDefinition 的配置信息为蓝本创建 RootBeanDefinition,也就是“已合并的 BeanDefinition”
mbd = new RootBeanDefinition(pbd);
// 用子 BeanDefinition 中的属性覆盖父 BeanDefinition 中的属性
mbd.overrideFrom(bd);
}

// 如果用户未配置 scope 属性,则默认将该属性配置为 singleton
if (!StringUtils.hasLength(mbd.getScope())) {
mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
}

if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
mbd.setScope(containingBd.getScope());
}

if (containingBd == null && isCacheBeanMetadata()) {
// 缓存合并后的 BeanDefinition
this.mergedBeanDefinitions.put(beanName, mbd);
}
}

return mbd;
}
}

上面的源码虽然有点长,但好在逻辑不是很复杂。加上我在源码里进行了比较详细的注解,我想耐心看一下还是可以看懂的,这里就不多说了。

2.5 从 FactoryBean 中获取 bean 实例

在经过前面这么多的步骤处理后,到这里差不多就接近 doGetBean 方法的尾声了。在本节中,我们来看看从 FactoryBean 实现类中获取 bean 实例的过程。关于 FactoryBean 的用法,我在导读那篇文章中已经演示过,这里就不再次说明了。那接下来,我们直入主题吧,相关的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

// 如果 name 以 & 开头,但 beanInstance 却不是 FactoryBean,则认为有问题。
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}

/*
* 如果上面的判断通过了,表明 beanInstance 可能是一个普通的 bean,也可能是一个
* FactoryBean。如果是一个普通的 bean,这里直接返回 beanInstance 即可。如果是
* FactoryBean,则要调用工厂方法生成一个 bean 实例。
*/
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}

Object object = null;
if (mbd == null) {
/*
* 如果 mbd 为空,则从缓存中加载 bean。FactoryBean 生成的单例 bean 会被缓存
* 在 factoryBeanObjectCache 集合中,不用每次都创建
*/
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// 经过前面的判断,到这里可以保证 beanInstance 是 FactoryBean 类型的,所以可以进行类型转换
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// 如果 mbd 为空,则判断是否存在名字为 beanName 的 BeanDefinition
if (mbd == null && containsBeanDefinition(beanName)) {
// 合并 BeanDefinition
mbd = getMergedLocalBeanDefinition(beanName);
}
// synthetic 字面意思是"合成的"。通过全局查找,我发现在 AOP 相关的类中会将该属性设为 true。
// 所以我觉得该字段可能表示某个 bean 是不是被 AOP 增强过,也就是 AOP 基于原始类合成了一个新的代理类。
// 不过目前只是猜测,没有深究。如果有朋友知道这个字段的具体意义,还望不吝赐教
boolean synthetic = (mbd != null && mbd.isSynthetic());

// 调用 getObjectFromFactoryBean 方法继续获取实例
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
/*
* FactoryBean 也有单例和非单例之分,针对不同类型的 FactoryBean,这里有两种处理方式:
* 1. 单例 FactoryBean 生成的 bean 实例也认为是单例类型。需放入缓存中,供后续重复使用
* 2. 非单例 FactoryBean 生成的 bean 实例则不会被放入缓存中,每次都会创建新的实例
*/
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 从缓存中取 bean 实例,避免多次创建 bean 实例
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 使用工厂对象中创建实例
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
// shouldPostProcess 等价于上一个方法中的 !synthetic,用于表示是否应用后置处理
if (object != null && shouldPostProcess) {
if (isSingletonCurrentlyInCreation(beanName)) {
return object;
}
beforeSingletonCreation(beanName);
try {
// 应用后置处理
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
afterSingletonCreation(beanName);
}
}
// 这里的 beanName 对应于 FactoryBean 的实现类, FactoryBean 的实现类也会被实例化,并被缓存在 singletonObjects 中
if (containsSingleton(beanName)) {
// FactoryBean 所创建的实例会被缓存在 factoryBeanObjectCache 中,供后续调用使用
this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
}
}
}
return (object != NULL_OBJECT ? object : null);
}
}
// 获取非单例实例
else {
// 从工厂类中获取实例
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (object != null && shouldPostProcess) {
try {
// 应用后置处理
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {

Object object;
try {
// if 分支的逻辑是 Java 安全方面的代码,可以忽略,直接看 else 分支的代码
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
return factory.getObject();
}
}, acc);
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
// 调用工厂方法生成 bean 实例
object = factory.getObject();
}
}
catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}

if (object == null && isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(
beanName, "FactoryBean which is currently in creation returned null from getObject");
}
return object;
}

上面的源码分析完了,代码虽长,但整体逻辑不是很复杂,这里简单总结一下。getObjectForBeanInstance 及它所调用的方法主要做了如下几件事情:

  1. 检测参数 beanInstance 的类型,如果是非 FactoryBean 类型的 bean,直接返回
  2. 检测 FactoryBean 实现类是否单例类型,针对单例和非单例类型进行不同处理
  3. 对于单例 FactoryBean,先从缓存里获取 FactoryBean 生成的实例
  4. 若缓存未命中,则调用 FactoryBean.getObject() 方法生成实例,并放入缓存中
  5. 对于非单例的 FactoryBean,每次直接创建新的实例即可,无需缓存
  6. 如果 shouldPostProcess = true,不管是单例还是非单例 FactoryBean 生成的实例,都要进行后置处理

本节涉及到了 FactoryBean 和后置处理两个特性,关于这两个特性,不熟悉的同学可以参考我在导读一文中的说明,这里就不过多解释了。

3. 总结

到这里,Spring IOC 容器获取 bean 实例这一块的内容就分析完了。如果大家是初次阅读 Spring 的源码,看不懂也没关系。多看几遍,认证思考一下,相信是能看得懂的。另外由于本人水平有限,以上的源码分析有误的地方,还望多指教,谢了。

好了,本文先到这里。又到周五了,祝大家在即将到来的周末玩的开心。over.

CreateBean 源码分析

2.1 创建 bean 实例的入口

在正式分析createBean(String, RootBeanDefinition, Object[])方法前,我们先来看看 createBean 方法是在哪里被调用的。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public T doGetBean(...) {
// 省略不相关代码
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 省略不相关代码
}

上面是 doGetBean 方法的代码片段,从中可以发现 createBean 方法。createBean 方法被匿名工厂类的 getObject 方法包裹,但这个匿名工厂类对象并未直接调用 getObject 方法。而是将自身作为参数传给了getSingleton(String, ObjectFactory)方法,那么我们接下来再去看看一下getSingleton(String, ObjectFactory) 方法的实现。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
// 从缓存中获取单例 bean,若不为空,则直接返回,不用再初始化
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
/*
* 将 beanName 添加到 singletonsCurrentlyInCreation 集合中,
* 用于表明 beanName 对应的 bean 正在创建中
*/
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<Exception>();
}
try {
// 通过 getObject 方法调用 createBean 方法创建 bean 实例
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// 将 beanName 从 singletonsCurrentlyInCreation 移除
afterSingletonCreation(beanName);
}
if (newSingleton) {
/*
* 将 <beanName, singletonObject> 键值对添加到 singletonObjects 集合中,
* 并从其他集合(比如 earlySingletonObjects)中移除 singletonObject 记录
*/
addSingleton(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}

上面的方法逻辑不是很复杂,这里简单总结一下。如下:

  1. 先从 singletonObjects 集合获取 bean 实例,若不为空,则直接返回
  2. 若为空,进入创建 bean 实例阶段。先将 beanName 添加到 singletonsCurrentlyInCreation
  3. 通过 getObject 方法调用 createBean 方法创建 bean 实例
  4. 将 beanName 从 singletonsCurrentlyInCreation 集合中移除
  5. 将 <beanName, singletonObject> 映射缓存到 singletonObjects 集合中

从上面的分析中,我们知道了 createBean 方法在何处被调用的。那么接下来我们一起深入 createBean 方法的源码中,来看看这个方法具体都做了什么事情。

2.2 createBean 方法全貌

createBean 和 getBean 方法类似,基本上都是空壳方法,只不过 createBean 的逻辑稍微多点,多做了一些事情。下面我们一起看看这个方法的实现逻辑,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;

// 解析 bean 的类型
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}

try {
// 处理 lookup-method 和 replace-method 配置,Spring 将这两个配置统称为 override method
mbdToUse.prepareMethodOverrides();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}

try {
// 在 bean 初始化前应用后置处理,如果后置处理返回的 bean 不为空,则直接返回
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}

// 调用 doCreateBean 创建 bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}

上面的代码不长,代码的执行流程比较容易看出,这里罗列一下:

  1. 解析 bean 类型
  2. 处理 lookup-method 和 replace-method 配置
  3. 在 bean 初始化前应用后置处理,若后置处理返回的 bean 不为空,则直接返回
  4. 若上一步后置处理返回的 bean 为空,则调用 doCreateBean 创建 bean 实例

下面我会分节对第2、3和4步的流程进行分析,步骤1的详细实现大家有兴趣的话,就自己去看看吧。

2.2.1 验证和准备 override 方法

当用户配置了 lookup-method 和 replace-method 时,Spring 需要对目标 bean 进行增强。在增强之前,需要做一些准备工作,也就是 prepareMethodOverrides 中的逻辑。下面来看看这个方法的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
MethodOverrides methodOverrides = getMethodOverrides();
if (!methodOverrides.isEmpty()) {
Set<MethodOverride> overrides = methodOverrides.getOverrides();
synchronized (overrides) {
// 循环处理每个 MethodOverride 对象
for (MethodOverride mo : overrides) {
prepareMethodOverride(mo);
}
}
}
}

protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
// 获取方法名为 mo.getMethodName() 的方法数量,当方法重载时,count 的值就会大于1
int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
// count = 0,表明根据方法名未找到相应的方法,此时抛出异常
if (count == 0) {
throw new BeanDefinitionValidationException(
"Invalid method override: no method with name '" + mo.getMethodName() +
"' on class [" + getBeanClassName() + "]");
}
// 若 count = 1,表明仅存在已方法名为 mo.getMethodName(),这意味着方法不存在重载
else if (count == 1) {
// 方法不存在重载,则将 overloaded 成员变量设为 false
mo.setOverloaded(false);
}
}

上面的源码中,prepareMethodOverrides方法循环调用了prepareMethodOverride方法,并没其他的太多逻辑。主要准备工作都是在 prepareMethodOverride 方法中进行的,所以我们重点关注一下这个方法。prepareMethodOverride 这个方法主要用于获取指定方法的方法数量 count,并根据 count 的值进行相应的处理。count = 0 时,表明方法不存在,此时抛出异常。count = 1 时,设置 MethodOverride 对象的overloaded成员变量为 false。这样做的目的在于,提前标注名称mo.getMethodName()的方法不存在重载,在使用 CGLIB 增强阶段就不需要进行校验,直接找到某个方法进行增强即可。

上面的方法没太多的逻辑,比较简单,就先分析到这里。

2.2.2 bean 实例化前的后置处理

后置处理是 Spring 的一个拓展点,用户通过实现 BeanPostProcessor 接口,并将实现类配置到 Spring 的配置文件中(或者使用注解),即可在 bean 初始化前后进行自定义操作。关于后置处理较为详细的说明,可以参考我的了一篇文章Spring IOC 容器源码分析系列文章导读,这里就不赘述了。下面我们来看看 createBean 方法中的后置处理逻辑,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
// 检测是否解析过,mbd.beforeInstantiationResolved 的值在下面的代码中会被设置
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
// 应用前置处理
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
// 应用后置处理
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
// 设置 mbd.beforeInstantiationResolved
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}

protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
// InstantiationAwareBeanPostProcessor 一般在 Spring 框架内部使用,不建议用户直接使用
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// bean 初始化前置处理
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {

Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
// bean 初始化后置处理
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}

在 resolveBeforeInstantiation 方法中,当前置处理方法返回的 bean 不为空时,后置处理才会被执行。前置处理器是 InstantiationAwareBeanPostProcessor 类型的,该种类型的处理器一般用在 Spring 框架内部,比如 AOP 模块中的AbstractAutoProxyCreator抽象类间接实现了这个接口中的 postProcessBeforeInstantiation方法,所以 AOP 可以在这个方法中生成为目标类的代理对象。不过我在调试的过程中,发现 AOP 在此处生成代理对象是有条件的。一般情况下条件都不成立,也就不会在此处生成代理对象。至于这个条件为什么不成立,因 AOP 这一块的源码我还没来得及看,所以暂时还无法解答。等我看过 AOP 模块的源码后,我再来尝试分析这个条件。

2.2.3 调用 doCreateBean 方法创建 bean

这一节,我们来分析一下doCreateBean方法的源码。在 Spring 中,做事情的方法基本上都是以do开头的,doCreateBean 也不例外。那下面我们就来看看这个方法都做了哪些事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {

/*
* BeanWrapper 是一个基础接口,由接口名可看出这个接口的实现类用于包裹 bean 实例。
* 通过 BeanWrapper 的实现类可以方便的设置/获取 bean 实例的属性
*/
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
// 从缓存中获取 BeanWrapper,并清理相关记录
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
/*
* 创建 bean 实例,并将实例包裹在 BeanWrapper 实现类对象中返回。createBeanInstance
* 中包含三种创建 bean 实例的方式:
* 1. 通过工厂方法创建 bean 实例
* 2. 通过构造方法自动注入(autowire by constructor)的方式创建 bean 实例
* 3. 通过无参构造方法方法创建 bean 实例
*
* 若 bean 的配置信息中配置了 lookup-method 和 replace-method,则会使用 CGLIB
* 增强 bean 实例。关于这个方法,后面会专门写一篇文章介绍,这里先说这么多。
*/
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 此处的 bean 可以认为是一个原始的 bean 实例,暂未填充属性
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
mbd.resolvedTargetType = beanType;

// 这里又遇到后置处理了,此处的后置处理是用于处理已“合并的 BeanDefinition”。关于这种后置处理器具体的实现细节就不深入理解了,大家有兴趣可以自己去看
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}

/*
* earlySingletonExposure 是一个重要的变量,这里要说明一下。该变量用于表示是否提前暴露
* 单例 bean,用于解决循环依赖。earlySingletonExposure 由三个条件综合而成,如下:
* 条件1:mbd.isSingleton() - 表示 bean 是否是单例类型
* 条件2:allowCircularReferences - 是否允许循环依赖
* 条件3:isSingletonCurrentlyInCreation(beanName) - 当前 bean 是否处于创建的状态中
*
* earlySingletonExposure = 条件1 && 条件2 && 条件3
* = 单例 && 是否允许循环依赖 && 是否存于创建状态中。
*/
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 添加工厂对象到 singletonFactories 缓存中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
// 获取早期 bean 的引用,如果 bean 中的方法被 AOP 切点所匹配到,此时 AOP 相关逻辑会介入
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}

Object exposedObject = bean;
try {
// 向 bean 实例中填充属性,populateBean 方法也是一个很重要的方法,后面会专门写文章分析
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
/*
* 进行余下的初始化工作,详细如下:
* 1. 判断 bean 是否实现了 BeanNameAware、BeanFactoryAware、
* BeanClassLoaderAware 等接口,并执行接口方法
* 2. 应用 bean 初始化前置操作
* 3. 如果 bean 实现了 InitializingBean 接口,则执行 afterPropertiesSet
* 方法。如果用户配置了 init-method,则调用相关方法执行自定义初始化逻辑
* 4. 应用 bean 初始化后置操作
*
* 另外,AOP 相关逻辑也会在该方法中织入切面逻辑,此时的 exposedObject 就变成了
* 一个代理对象了
*/
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}

if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 若 initializeBean 方法未改变 exposedObject 的引用,则此处的条件为 true。
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// 下面的逻辑我也没完全搞懂,就不分析了。见谅。
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}

try {
// 注册销毁逻辑
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}

return exposedObject;
}

上面的注释比较多,分析的应该比较详细的。不过有一部分代码我暂时没看懂,就不分析了,见谅。下面我们来总结一下 doCreateBean 方法的执行流程吧,如下:

  1. 从缓存中获取 BeanWrapper 实现类对象,并清理相关记录
  2. 若未命中缓存,则创建 bean 实例,并将实例包裹在 BeanWrapper 实现类对象中返回
  3. 应用 MergedBeanDefinitionPostProcessor 后置处理器相关逻辑
  4. 根据条件决定是否提前暴露 bean 的早期引用(early reference),用于处理循环依赖问题
  5. 调用 populateBean 方法向 bean 实例中填充属性
  6. 调用 initializeBean 方法完成余下的初始化工作
  7. 注册销毁逻辑

doCreateBean 方法的流程比较复杂,步骤略多。由此也可了解到创建一个 bean 还是很复杂的,这中间要做的事情繁多。比如填充属性、对 BeanPostProcessor 拓展点提供支持等。以上的步骤对应的方法具体是怎样实现的,本篇文章并不打算展开分析。在后续的文章中,我会单独写文章分析几个逻辑比较复杂的步骤。有兴趣的阅读的朋友可以稍微等待一下,相关文章本周会陆续进行更新。

3. 总结

到这里,createBean 方法及其所调用的方法的源码就分析完了。总的来说,createBean 方法还是比较复杂的,需要多看几遍才能理清一些头绪。由于 createBean 方法比较复杂,对于以上的源码分析,我并不能保证不出错。如果有写错的地方,还请大家指点迷津。毕竟当局者迷,作为作者,我很难意识到哪里写的有问题。

好了,本篇文章到此结束。谢谢阅读。

循环依赖

1. 简介

本文,我们来看一下 Spring 是如何解决循环依赖问题的。在本篇文章中,我会首先向大家介绍一下什么是循环依赖。然后,进入源码分析阶段。为了更好的说明 Spring 解决循环依赖的办法,我将会从获取 bean 的方法getBean(String)开始,把整个调用过程梳理一遍。梳理完后,再来详细分析源码。通过这几步的讲解,希望让大家能够弄懂什么是循环依赖,以及如何解循环依赖。

循环依赖相关的源码本身不是很复杂,不过这里要先介绍大量的前置知识。不然这些源码看起来很简单,但读起来可能却也不知所云。那下面我们先来了解一下什么是循环依赖。

2. 背景知识

2.1 什么是循环依赖

所谓的循环依赖是指,A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。它们之间的依赖关系如下:

img

这里以两个类直接相互依赖为例,他们的实现代码可能如下:

1
2
3
4
5
6
7
8
public class BeanB {
private BeanA beanA;
// 省略 getter/setter
}

public class BeanA {
private BeanB beanB;
}

配置信息如下:

1
2
3
4
5
6
<bean id="beanA" class="xyz.coolblog.BeanA">
<property name="beanB" ref="beanB"/>
</bean>
<bean id="beanB" class="xyz.coolblog.BeanB">
<property name="beanA" ref="beanA"/>
</bean>

IOC 容器在读到上面的配置时,会按照顺序,先去实例化 beanA。然后发现 beanA 依赖于 beanB,接在又去实例化 beanB。实例化 beanB 时,发现 beanB 又依赖于 beanA。如果容器不处理循环依赖的话,容器会无限执行上面的流程,直到内存溢出,程序崩溃。当然,Spring 是不会让这种情况发生的。在容器再次发现 beanB 依赖于 beanA 时,容器会获取 beanA 对象的一个早期的引用(early reference),并把这个早期引用注入到 beanB 中,让 beanB 先完成实例化。beanB 完成实例化,beanA 就可以获取到 beanB 的引用,beanA 随之完成实例化。这里大家可能不知道“早期引用”是什么意思,这里先别着急,我会在下一章进行说明。

好了,本章先到这里,我们继续往下看。

2.2 一些缓存的介绍

在进行源码分析前,我们先来看一组缓存的定义。如下:

1
2
3
4
5
6
7
8
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);

/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

根据缓存变量上面的注释,大家应该能大致了解他们的用途。我这里简单说明一下吧:

缓存 用途
singletonObjects 用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
earlySingletonObjects 存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
singletonFactories 存放 bean 工厂对象,用于解决循环依赖

上一章提到了”早期引用“,所谓的”早期引用“是指向原始对象的引用。所谓的原始对象是指刚创建好的对象,但还未填充属性。这样讲大家不知道大家听明白了没,不过没听明白也不要紧。简单做个实验就知道了,这里我们先定义一个对象 Room:

1
2
3
4
5
6
7
8
/** Room 包含了一些电器 */
public class Room {
private String television;
private String airConditioner;
private String refrigerator;
private String washer;
// 省略 getter/setter
}

配置如下:

1
2
3
4
5
6
<bean id="room" class="xyz.coolblog.demo.Room">
<property name="television" value="Xiaomi"/>
<property name="airConditioner" value="Gree"/>
<property name="refrigerator" value="Haier"/>
<property name="washer" value="Siemens"/>
</bean>

我们先看一下完全实例化好后的 bean 长什么样的。如下:

img

从调试信息中可以看得出,Room 的每个成员变量都被赋上值了。然后我们再来看一下“原始的 bean 对象”长的是什么样的,如下:

img

结果比较明显了,所有字段都是 null。这里的 bean 和上面的 bean 指向的是同一个对象Room@1567,但现在这个对象所有字段都是 null,我们把这种对象成为原始的对象。形象点说,上面的 bean 对象是一个装修好的房子,可以拎包入住了。而这里的 bean 对象还是个毛坯房,还要装修一下(填充属性)才行。

2.3 回顾获取 bean 的过程

本节,我们来了解从 Spring IOC 容器中获取 bean 实例的流程(简化版),这对我们后续的源码分析会有比较大的帮助。先看图:

img

先来简单介绍一下这张图,这张图是一个简化后的流程图。开始流程图中只有一条执行路径,在条件 sharedInstance != null 这里出现了岔路,形成了绿色和红色两条路径。在上图中,读取/添加缓存的方法我用蓝色的框和☆标注了出来。至于虚线的箭头,和虚线框里的路径,这个下面会说到。

我来按照上面的图,分析一下整个流程的执行顺序。这个流程从 getBean 方法开始,getBean 是个空壳方法,所有逻辑都在 doGetBean 方法中。doGetBean 首先会调用 getSingleton(beanName) 方法获取 sharedInstance,sharedInstance 可能是完全实例化好的 bean,也可能是一个原始的 bean,当然也有可能是 null。如果不为 null,则走绿色的那条路径。再经 getObjectForBeanInstance 这一步处理后,绿色的这条执行路径就结束了。

我们再来看一下红色的那条执行路径,也就是 sharedInstance = null 的情况。在第一次获取某个 bean 的时候,缓存中是没有记录的,所以这个时候要走创建逻辑。上图中的 getSingleton(beanName, new ObjectFactory<Object>() {...}) 方法会创建一个 bean 实例,上图虚线路径指的是 getSingleton 方法内部调用的两个方法,其逻辑如下:

1
2
3
4
5
6
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
// 省略部分代码
singletonObject = singletonFactory.getObject();
// ...
addSingleton(beanName, singletonObject);
}

如上所示,getSingleton 会在内部先调用 getObject 方法创建 singletonObject,然后再调用 addSingleton 将 singletonObject 放入缓存中。getObject 在内部代用了 createBean 方法,createBean 方法基本上也属于空壳方法,更多的逻辑是写在 doCreateBean 方法中的。doCreateBean 方法中的逻辑很多,其首先调用了 createBeanInstance 方法创建了一个原始的 bean 对象,随后调用 addSingletonFactory 方法向缓存中添加单例 bean 工厂,从该工厂可以获取原始对象的引用,也就是所谓的“早期引用”。再之后,继续调用 populateBean 方法向原始 bean 对象中填充属性,并解析依赖。getObject 执行完成后,会返回完全实例化好的 bean。紧接着再调用 addSingleton 把完全实例化好的 bean 对象放入缓存中。到这里,红色执行路径差不多也就要结束的。

我这里没有把 getObject、addSingleton 方法和 getSingleton(String, ObjectFactory) 并列画在红色的路径里,目的是想简化一下方法的调用栈(都画进来有点复杂)。我们可以进一步简化上面的调用流程,比如下面:

img

这个流程看起来是不是简单多了,命中缓存走绿色路径,未命中走红色的创建路径。好了,本节先到这。

3. 源码分析

好了,经过前面的铺垫,现在我们终于可以深入源码一探究竟了,想必大家已等不及了。那我不卖关子了,下面我们按照方法的调用顺序,依次来看一下循环依赖相关的代码。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {

// ......

// 从缓存中获取 bean 实例
Object sharedInstance = getSingleton(beanName);

// ......
}

public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 从 singletonObjects 获取实例,singletonObjects 中的实例都是准备好的 bean 实例,可以直接使用
Object singletonObject = this.singletonObjects.get(beanName);
// 判断 beanName 对应的 bean 是否正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从 earlySingletonObjects 中获取提前曝光的 bean
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 获取相应的 bean 工厂
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 提前曝光 bean 实例(raw bean),用于解决循环依赖
singletonObject = singletonFactory.getObject();

// 将 singletonObject 放入缓存中,并将 singletonFactory 从缓存中移除
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

上面的源码中,doGetBean 所调用的方法 getSingleton(String) 是一个空壳方法,其主要逻辑在 getSingleton(String, boolean) 中。该方法逻辑比较简单,首先从 singletonObjects 缓存中获取 bean 实例。若未命中,再去 earlySingletonObjects 缓存中获取原始 bean 实例。如果仍未命中,则从 singletonFactory 缓存中获取 ObjectFactory 对象,然后再调用 getObject 方法获取原始 bean 实例的应用,也就是早期引用。获取成功后,将该实例放入 earlySingletonObjects 缓存中,并将 ObjectFactory 对象从 singletonFactories 移除。看完这个方法,我们再来看看 getSingleton(String, ObjectFactory) 方法,这个方法也是在 doGetBean 中被调用的。这次我会把 doGetBean 的代码多贴一点出来,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {

// ......
Object bean;

// 从缓存中获取 bean 实例
Object sharedInstance = getSingleton(beanName);

// 这里先忽略 args == null 这个条件
if (sharedInstance != null && args == null) {
// 进行后续的处理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// ......

// mbd.isSingleton() 用于判断 bean 是否是单例模式
if (mbd.isSingleton()) {
// 再次获取 bean 实例
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
// 创建 bean 实例,createBean 返回的 bean 是完全实例化好的
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
// 进行后续的处理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

// ......
}

// ......

// 返回 bean
return (T) bean;
}

这里的代码逻辑和我在 2.3 回顾获取 bean 的过程 一节的最后贴的主流程图已经很接近了,对照那张图和代码中的注释,大家应该可以理解 doGetBean 方法了。继续往下看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {

// ......

// 调用 getObject 方法创建 bean 实例
singletonObject = singletonFactory.getObject();
newSingleton = true;

if (newSingleton) {
// 添加 bean 到 singletonObjects 缓存中,并从其他集合中将 bean 相关记录移除
addSingleton(beanName, singletonObject);
}

// ......

// 返回 singletonObject
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}

protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 将 <beanName, singletonObject> 映射存入 singletonObjects 中
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));

// 从其他缓存中移除 beanName 相关映射
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}

上面的代码中包含两步操作,第一步操作是调用 getObject 创建 bean 实例,第二步是调用 addSingleton 方法将创建好的 bean 放入缓存中。代码逻辑并不复杂,相信大家都能看懂。那么接下来我们继续往下看,这次分析的是 doCreateBean 中的一些逻辑。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
throws BeanCreationException {

BeanWrapper instanceWrapper = null;

// ......

// ☆ 创建 bean 对象,并将 bean 对象包裹在 BeanWrapper 对象中返回
instanceWrapper = createBeanInstance(beanName, mbd, args);

// 从 BeanWrapper 对象中获取 bean 对象,这里的 bean 指向的是一个原始的对象
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);

/*
* earlySingletonExposure 用于表示是否”提前暴露“原始对象的引用,用于解决循环依赖。
* 对于单例 bean,该变量一般为 true。更详细的解释可以参考我之前的文章
*/
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// ☆ 添加 bean 工厂对象到 singletonFactories 缓存中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
/*
* 获取原始对象的早期引用,在 getEarlyBeanReference 方法中,会执行 AOP
* 相关逻辑。若 bean 未被 AOP 拦截,getEarlyBeanReference 原样返回
* bean,所以大家可以把
* return getEarlyBeanReference(beanName, mbd, bean)
* 等价于:
* return bean;
*/
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}

Object exposedObject = bean;

// ......

// ☆ 填充属性,解析依赖
populateBean(beanName, mbd, instanceWrapper);

// ......

// 返回 bean 实例
return exposedObject;
}

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
// 将 singletonFactory 添加到 singletonFactories 缓存中
this.singletonFactories.put(beanName, singletonFactory);

// 从其他缓存中移除相关记录,即使没有
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}

上面的代码简化了不少,不过看起来仍有点复杂。好在,上面代码的主线逻辑比较简单,由三个方法组成。如下:

1
2
3
4
1. 创建原始 bean 实例 → createBeanInstance(beanName, mbd, args)
2. 添加原始对象工厂对象到 singletonFactories 缓存中
→ addSingletonFactory(beanName, new ObjectFactory<Object>{...})
3. 填充属性,解析依赖 → populateBean(beanName, mbd, instanceWrapper)

到这里,本节涉及到的源码就分析完了。可是看完源码后,我们似乎仍然不知道这些源码是如何解决循环依赖问题的。难道本篇文章就到这里了吗?答案是否。下面我来解答这个问题,这里我还是以 BeanA 和 BeanB 两个类相互依赖为例。在上面的方法调用中,有几个关键的地方,下面一一列举出来:

1. 创建原始 bean 对象

1
2
instanceWrapper = createBeanInstance(beanName, mbd, args);
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);

假设 beanA 先被创建,创建后的原始对象为 BeanA@1234,上面代码中的 bean 变量指向就是这个对象。

2. 暴露早期引用

1
2
3
4
5
6
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});

beanA 指向的原始对象创建好后,就开始把指向原始对象的引用通过 ObjectFactory 暴露出去。getEarlyBeanReference 方法的第三个参数 bean 指向的正是 createBeanInstance 方法创建出原始 bean 对象 BeanA@1234。

3. 解析依赖

1
populateBean(beanName, mbd, instanceWrapper);

populateBean 用于向 beanA 这个原始对象中填充属性,当它检测到 beanA 依赖于 beanB 时,会首先去实例化 beanB。beanB 在此方法处也会解析自己的依赖,当它检测到 beanA 这个依赖,于是调用 BeanFactry.getBean(“beanA”) 这个方法,从容器中获取 beanA。

4. 获取早期引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// ☆ 从缓存中获取早期引用
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// ☆ 从 SingletonFactory 中获取早期引用
singletonObject = singletonFactory.getObject();

this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

接着上面的步骤讲,populateBean 调用 BeanFactry.getBean(“beanA”) 以获取 beanB 的依赖。getBean(“beanA”) 会先调用 getSingleton(“beanA”),尝试从缓存中获取 beanA。此时由于 beanA 还没完全实例化好,于是 this.singletonObjects.get(“beanA”) 返回 null。接着 this.earlySingletonObjects.get(“beanA”) 也返回空,因为 beanA 早期引用还没放入到这个缓存中。最后调用 singletonFactory.getObject() 返回 singletonObject,此时 singletonObject != null。singletonObject 指向 BeanA@1234,也就是 createBeanInstance 创建的原始对象。此时 beanB 获取到了这个原始对象的引用,beanB 就能顺利完成实例化。beanB 完成实例化后,beanA 就能获取到 beanB 所指向的实例,beanA 随之也完成了实例化工作。由于 beanB.beanA 和 beanA 指向的是同一个对象 BeanA@1234,所以 beanB 中的 beanA 此时也处于可用状态了。

以上的过程对应下面的流程图:

img

4. 总结

到这里,本篇文章差不多就快写完了,不知道大家看懂了没。这篇文章在前面做了大量的铺垫,然后再进行源码分析。相比于我之前写的几篇文章,本篇文章所对应的源码难度上比之前简单一些。但说实话也不好写,我本来只想简单介绍一下背景知识,然后直接进行源码分析。但是又怕有的朋友看不懂,所以还是用了大篇幅介绍的背景知识。这样写,可能有的朋友觉得比较啰嗦。但是考虑到大家的水平不一,为了保证让大家能够更好的理解,所以还是尽量写的详细一点。本篇文章总的来说写的还是有点累的,花了一些心思思考怎么安排章节顺序,怎么简化代码和画图。如果大家看完这篇文章,觉得还不错的话,不妨给个赞吧,也算是对我的鼓励吧。

由于个人的技术能力有限,若文章有错误不妥之处,欢迎大家指出来。好了,本篇文章到此结束,谢谢大家的阅读。

初始化的工作

1. 简介

本篇文章是“Spring IOC 容器源码分析”系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bean 做最后的初始化工作。相较于之前几篇文章所分析的源码,initializeBean 的源码相对比较简单,大家可以愉快的阅读。好了,其他的不多说了,我们直入主题吧。

2. 源码分析

本章我们来分析一下 initializeBean 方法的源码。在完成分析后,还是像往常一样,把方法的执行流程列出来。好了,看源码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareMethods(beanName, bean);
return null;
}
}, getAccessControlContext());
}
else {
// 若 bean 实现了 BeanNameAware、BeanFactoryAware、BeanClassLoaderAware 等接口,则向 bean 中注入相关对象
invokeAwareMethods(beanName, bean);
}

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 执行 bean 初始化前置操作
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

try {
/*
* 调用初始化方法:
* 1. 若 bean 实现了 InitializingBean 接口,则调用 afterPropertiesSet 方法
* 2. 若用户配置了 bean 的 init-method 属性,则调用用户在配置中指定的方法
*/
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// 执行 bean 初始化后置操作,AOP 会在此处向目标对象中织入切面逻辑
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}

以上就是 initializeBean 方法的逻辑,很简单是不是。该方法做了如下几件事情:

  1. 检测 bean 是否实现了 *Aware 类型接口,若实现,则向 bean 中注入相应的对象
  2. 执行 bean 初始化前置操作
  3. 执行初始化操作
  4. 执行 bean 初始化后置操作

在上面的流程中,我们又发现了后置处理器的踪影。如果大家阅读过 Spring 的源码,会发现后置处理器在 Spring 源码中多次出现过。后置处理器是 Spring 拓展点之一,通过实现后置处理器 BeanPostProcessor 接口,我们就可以插手 bean 的初始化过程。比如大家所熟悉的 AOP 就是在后置处理 postProcessAfterInitialization 方法中向目标对象中织如切面逻辑的。关于“前置处理”和“后置处理”相关的源码,这里就不分析了,大家有兴趣自己去看一下。接下来分析一下 invokeAwareMethods 和 invokeInitMethods 方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
// 注入 beanName 字符串
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
// 注入 ClassLoader 对象
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}
if (bean instanceof BeanFactoryAware) {
// 注入 BeanFactory 对象
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}

invokeAwareMethods 方法的逻辑很简单,一句话总结:根据 bean 所实现的 Aware 的类型,向 bean 中注入不同类型的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {

// 检测 bean 是否是 InitializingBean 类型的
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
((InitializingBean) bean).afterPropertiesSet();
return null;
}
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
// 如果 bean 实现了 InitializingBean,则调用 afterPropertiesSet 方法执行初始化逻辑
((InitializingBean) bean).afterPropertiesSet();
}
}

if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// 调用用户自定义的初始化方法
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}

invokeInitMethods 方法用于执行初始化方法,也不复杂,就不多说了。