在Spring IOC 容器 和 Spring bean 一文中,介绍了Spring IOC 容器和bean 一些相关基础知识。
Spring IOC 容器的作用是 管理 Bean 的生命周期,控制 Bean 的依赖注入。
本文内容将具体介绍Spring IOC 容器是如何管理bean 的生命周期和bean 之间的依赖关系的。
先来看一张整体框架图
读取Bean配置信息:Spring容器首先读取Bean的配置信息,这些配置信息可以来自XML配置文件、Java类(带有
@Configuration
注解)或通过注解(如@Autowired
)的方式定义。注册Bean定义:根据读取的配置信息,Spring将Bean的定义(包括类名、依赖关系等)注册到Bean定义注册表中。
实例化Bean:Spring根据Bean定义注册表中的信息,实例化相应的Bean。这个过程中会处理依赖注入和循环依赖的问题。
使用Bean:应用程序可以从Spring容器中获取Bean实例并使用它们。Spring容器会根据需要从Bean缓存池中返回已经实例化并初始化好的Bean。
以下代码分析基于Spring boot 2.3.4 版本进行分析。
直接从服务启动run 方法,快进到 refresh 方法看重点
1 | public class CodingInActionApplication{ |
1 | AbstractApplicationContext.java |
本文将重点介绍DefaultListableBeanFactory 和 finishBeanFactoryInitialization方法中整个流程
1.bean 容器-DefaultListableBeanFactory
AbstractApplicationContext.obtainFreshBeanFactory
根据前面的内容我们知道, ApplicationContext 是面向应用的容器,但在Spring 框架层面,BeanFactory才是IOC 容器。 根据代码可知,在Spring boot 的启动过程中, BeanFactory
使用的是DefaultListableBeanFactory
。
先来看下, BeanFactory作为Spring IOC 容器,有哪些变量来实现功能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
36public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
/** Map of bean definition objects, keyed by bean name. */
//存储所有注册的bean定义,键是bean的名称。
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
/** Map from bean name to merged BeanDefinitionHolder. */
//从bean名称到合并的`BeanDefinitionHolder`的映射。在处理bean定义继承和合并时,存储合并后的bean定义。
private final Map<String, BeanDefinitionHolder> mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256);
/** Map of singleton and non-singleton bean names, keyed by dependency type. */
//从依赖类型到单例和非单例bean名称的映射。用于根据类型查找所有相关的bean名称,包括单例和非单例bean
private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);
/** Map of singleton-only bean names, keyed by dependency type. */
//- 从依赖类型到单例bean名称的映射。用于根据类型查找所有单例bean名称,便于单例bean的管理和查找。
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64);
/** List of bean definition names, in registration order. */
//按注册顺序存储的bean定义名称列表。维护bean定义的注册顺序,便于按顺序处理bean定义。
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
/** List of names of manually registered singletons, in registration order. */
//按注册顺序存储的手动注册的单例名称集合。存储通过手动方式注册的单例bean,便于管理和查找。
private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);
/** Cached array of bean definition names in case of frozen configuration. */
private volatile String[] frozenBeanDefinitionNames;
/** Whether bean definition metadata may be cached for all beans. */
private volatile boolean configurationFrozen;
}
从以上DefaultListableBeanFactory 的变量可以看出, 维护了各种维度的beanDefinition
loadBeanDefinitions
是将解析后的bean定义按bean名称 -> bean定义
的逻辑存放到beanDefinitionMap
这个ConcurrentHashMap
中。- 同时,也会更新其他几个重要的
ConcurrentHashMap
,如mergedBeanDefinitionHolders
、allBeanNamesByType
、singletonBeanNamesByType
,以便于更高效的bean管理和依赖解析。 loadBeanDefinitions
方法不仅仅是存放bean定义,还包括解析、验证和注册多个步骤。beanDefinitionNames
列表也会在这个过程中被更新,以维护bean定义的注册顺序。
下面进入obtainFreshBeanFactory进行分析1
2
3
4
5AbstractApplicationContext.java
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
1.1 refreshBeanFactory
1 | AbstractRefreshableApplicationContext.java |
1.1.1 loadBeanDefinitions
具体过程待补充 :
可以参考
https://blog.csdn.net/andy_zhang2007/article/details/85381148
1.2 getBeanFactory
1 | AbstractRefreshableApplicationContext.java |
2. bean 的生命周期
在 finishBeanFactoryInitialization 方法中,管理的bean 都是 非懒加载单例bean1
2
3
4
5
6
7
8
9AbstractApplicationContext.java
public void refresh() throws BeansException, IllegalStateException {
// 1. 创建beanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 2. bean的生命周期管理和依赖关系的装配
finishBeanFactoryInitialization(beanFactory);
}
在Spring中,Bean的生命周期包括几个重要的阶段:
- 实例化(Instantiation): Bean被创建,通过调用构造方法实例化。
- 属性注入(Property Population): Spring通过依赖注入将Bean的依赖关系注入。
- 初始化(Initialization): 在所有属性被设置之后,Spring可以调用定制的初始化方法。
- 销毁(Destruction): 在Spring容器关闭之前,可以调用定制的销毁方法。
3. 三层缓存
根据上面的框架图可知, 可以看到实例化后bean 会放到缓存中。
既然使用缓存,其经典使用方式必然是“有就直接获取,没有就创建并回填缓存”, bean 缓存的使用过程也不例外, 同样遵循这一流程
不过在Spring 中, 这个缓存比较特殊的一点是由3层本地缓存构成。
想要了解三层缓存机制,首先需要了解各层缓存中存放的具体内容,下面来分析一下
1 | public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry |
singletonObjects
:存放init完成的单例bean, 此时的bean是成熟完整。earlySingletonObjects
:存储由singletonFactories
创建的早期引用,即提前暴露的对象(已经完成instantiation,但是还没有populate 、init)。singletonFactories
:存储用于生成早期单例bean引用的工厂对象。当一个单例bean正在创建过程中,还没有完成init 阶段是时(比如还没有注入依赖),就会暂时存入这个缓存。
earlySingletonObjects
和singletonFactories
一起完成bean 早期引用的管理, 早期引用是为了解决bean 的循环依赖
singletonObjects、earlySingletonObjects中的value 是Object,存放的也就是完整bean 引用,和早期bean 引用。下面重点分析比较陌生的singletonFactories。
3.1 函数式接口 ObjectFactory
要想singletonFactories 存放的内容,需要先了解函数式接口ObjectFactory
Java 8引入的函数式接口(Functional Interface)概念,是指仅有一个抽象方法的接口(除了Object
类的公有方法外)。这种接口允许被隐式转换为Lambda表达式。java.lang.FunctionalInterface
注解用于标记一个接口是函数式接口,这个注解不是必须的,但它可以帮助开发者和编译器验证接口是否满足函数式接口的条件。
Lambda表达式可以用来简洁地 implements 函数式接口。
在解决循环依赖时用到的singletonFactories, 其value 就是 ObjectFactory1
2
3
4
5
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
4.非懒加载的单例bean-getBean
Spring IOC 容器现在有了bean 的定义信息,就可以正式开始bean的生命周期管理和依赖关系的装配了。
实例化bean的入口就在前面提到的DefaultListableBeanFactory中, 根据bean 名称循环获取bean 实例
从doGetBean的逻辑中可以看到各种作用域的bean 其实都在这个方法中创建,例如singleton(单例) 、prototype(每次都创建新实例),
平时我们经常提到的三层缓存解决循环依赖,其实指的都是非懒加载的单例bean ,作用域是prototype 的bean 是无法用这种方式解决循环依赖的。
看下面的代码,由于作用域是prototype 的bean 并不需要三层缓存的参与,所以少了对于三层缓存的管理, 调用调研同一个createBean 创建新的实例。 所以下面我们重点讲解单例bean 的创建过程, 其他作用域的bean 自然就懂了。
1 | DefaultListableBeanFactory.java |
以下省略了部分代码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
114AbstractBeanFactory.java
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(
String name, boolean typeCheckOnly) Class<T> requiredType, Object[] args,
throws BeansException {
String beanName = transformedBeanName(name);
Object bean;
// 对于一个bean 走到这里有两种情况 1. bean 的正常创建流程 2. 循环依赖的情况
// 针对 bean 的正常创建流程, getSingleton 的返回是null
// 针对循环依赖触发的bean 获取,通过getSingleton可以获取到由三级缓存生成的二级缓存,早期引用,从而解决循环依赖问题。
// 先尝试从缓存中获取
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 缓存中存在则直接返回
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
try {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// 在创建某个 Bean 之前,必须先确保它所依赖的所有其他Bean 都已经创建并初始化。
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 注册所有依赖于当前bean 的bean 信息
// 该信息使用 Map<String, Set<String>> dependentBeanMap 记录
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// 单例bean的创建,缓存中没有则需要创建并回写缓存
if (mbd.isSingleton()) {
//
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// Prototype 是作用域,代表每次调用getBean从容器中获取对象时,都返回一个新的实例
// 所以 对于Prototype作用域,不会走getSingleton 使用3层缓存的逻辑, 直接调用createBean 生成新的实例即可
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
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, () -> {
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;
}
}
return (T) bean;
}
函数getSingleton有多个重载版本, 在上面这段代码中就涉及到2个1
2
3
4
5public Object getSingleton(String beanName) {
}
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {}
5. 从缓存中获取bean-getSingleton
getSingleton(String beanName) 的作用是从缓存中获取bean, 其具体逻辑还涉及到getSingleton的第3个重载版本,具体逻辑如下
5.1 缓存管理
1 | 代码均在 DefaultSingletonBeanRegistry.java |
5.2 双重校验锁
在用java 实现单例模式时,一种经典的方式是, 双检锁/双重校验锁(DCL,即 double-checked locking)
- 第一个检查在进入同步块之前,避免了不必要的同步。
- 第二个检查在进入同步块之后,确保实例在多线程环境中正确初始化。
1 | public class Singleton { |
在这个Object getSingleton(String beanName, boolean allowEarlyReference)
方法中,同样使用了 双重校验锁机制,但它包含更多的逻辑以处理 Spring 框架中的具体需求,特别是早期单例引用和单例工厂的处理。严格来说,它不完全是经典的双重校验锁模式,但其思想是一致的,即通过两次检查(一次在同步块外,一次在同步块内)来优化性能并确保线程安全。
这种设计在 Spring 框架中用于确保 Bean 的创建是线程安全的,同时尽可能减少同步开销,以提高性能。
6.缓存中不存在则创建-getSingleton
来看doGetBean 方法中,第2个getSingleton 方法
1 | protected <T> T doGetBean( |
6.1 ObjectFactory 的匿名类实现-createBean
查看这个getSingleton方法的完整定义,可以看到第二个参数是前面提到的函数式接口ObjectFactory,1
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {}
在缓存中不存在尝试去创建bean实例的逻辑中, 传入的参数如下
1 |
|
可以看到在传入getSingleton
的第二个参数时,使用lambda表达式传入了一个匿名类实现,该匿名类中的createBean
包含了实例化bean 的逻辑
Java编译器会自动将这个Lambda表达式转换成ObjectFactory
接口的一个匿名实现类。这个过程完全是自动的,背后的转换对开发者来说是透明的。
进入该重载版本看一下具体逻辑
1 | DefaultListableBeanFactory.java |
1 | singletonObject = singletonFactory.getObject(); |
当该getSingleton 逻辑走到 ObjectFactory.getObject
时,实际会调用到会走到上面匿名类的实现逻辑, 即会走到createBean逻辑, 即在getBean 方法中的匿名类
1 | sharedInstance = getSingleton(beanName, () -> { |
进入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
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
122AbstractAutowireCapableBeanFactory.java
protected Object createBean(String beanName, RootBeanDefinition mbd, { Object[] args)
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
//检查是否已经对 Bean 定义进行了后处理,如果没有,则调用 applyMergedBeanDefinitionPostProcessors 方法应用后处理器。
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;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
//如果允许循环引用,并且当前 Bean 是单例,则调用 addSingletonFactory 方法,提供一个回调以获取早期 Bean 引用。
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 添加第3级缓存, 此处第二个参数又是一个ObjectFactory的匿名类实现
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
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) {
// 尝试获取早期引用,那么这个早期引用肯定是因为循环依赖,其他bean在getSingleton(beanName, true)生产出来的
// 因为该bean 自己的生产过程中,只会主动添加一级、三级缓存
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 表示init后,Bean实例未被代理,在未代理的情况下,exposedObject 和 bean 是相同的引用
// 同时在理论上,getSingleton(beanName, false)获取到的应该也是相同的引用。
if (exposedObject == bean) {
// 如果存在早期暴露的单例引用,并且exposedObject未被代理,将exposedObject替换为早期暴露的单例引用。
// 理论上不存在代理时,earlySingletonReference bean exposedObject 三者应该是相同的
// exposedObject = earlySingletonReference这一赋值操作即使在大多数情况下看似多余,
// 但它确保了整个初始化过程的一致性,尤其在处理代理对象和循环依赖问题时,保证了最终暴露的Bean实例是正确的。
// 这种设计提高了代码的健壮性和可维护性,确保了在各种复杂场景下的正确性。
exposedObject = earlySingletonReference;
} // 存在代理操作时,要确保依赖当前bean 的其他bean 引用到了正确的版本
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
// 获取所有依赖当前Bean的Bean名称。
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
// 尝试移除只为类型检查而创建的单例Bean。如果成功移除,表示这个Bean只是为类型检查而创建的,不是实际使用的Bean。
// 通过这个循环,Spring会过滤掉那些只为类型检查而创建的Bean,保留那些实际依赖当前Bean的Bean。
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
// 这段逻辑确保在处理循环依赖时,确保依赖的Bean使用的是最终版本的Bean,而不是中间状态的原始Bean。
// 如果存在实际依赖当前Bean的Bean。抛出BeanCurrentlyInCreationException异常
// 因为这意味着有Bean在循环依赖的过程中使用了当前Bean的原始版本,但最终当前Bean被包装(如被AOP代理)。
// 这意味着被依赖的Bean使用的不是最终版本的Bean,这可能导致一些问题。
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 " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
// Register bean as disposable.
// 将一个Bean注册为可销毁的(disposable)
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
6.2 doCreateBean-createBeanInstance
使用反射创建bean 实例,整个过程可以分为三个步骤:
- 获取类的 Class 对象实例
- 根据 Class 对象实例获取 Constructor 对象, 这里默认使用无参构造函数, 等待后面的populate 填充属性
- 使用 Constructor 对象的 newInstance 方法获取反射类对象
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
68protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, { Object[] args)
// Make sure bean class is actually resolved at this point.
// 获取bean 的Class 对象,Class是通过反射创建bean 的基础
Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 检查 Bean 类是否为 public,如果不是且不允许非 public 访问,则抛出 BeanCreationException 异常。
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
// 如果 mbd 配置了一个实例提供者(Supplier),则通过该Supplier创建 Bean 实例。
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
// 如果配置了工厂方法名,则通过工厂方法创建 Bean 实例。
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
// 标志是否已经解析了构造函数或工厂方法。
boolean resolved = false;
//标志是否需要自动装配, 即bean之间是否有依赖关系
boolean autowireNecessary = false;
// 只有在没有传递构造函数参数的情况下,才会进行缓存检查。这是因为如果有参数传入,必然需要重新解析构造函数。
if (args == null) {
//进入同步块,确保对 mbd 的检查和修改是线程安全的。
synchronized (mbd.constructorArgumentLock) {
//检查 resolvedConstructorOrFactoryMethod 是否已经被解析并缓存。
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
//如果 resolved 为 true,则表示已经有缓存的构造函数
if (resolved) {
//根据 autowireNecessary 的值决定使用哪种方式创建 Bean 实例
//如果需要自动装配(autowireNecessary 为 true),则调用 autowireConstructor 方法,通过自动装配的方式创建 Bean 实例。
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
//如果不需要自动装配,则调用 instantiateBean 方法,使用无参构造函数创建 Bean 实例。
return instantiateBean(beanName, mbd);
}
}
// Candidate constructors for autowiring?
// 调用 BeanPostProcessor 确定候选的构造函数。
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if (ctors != null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
// 无参构造函数
return instantiateBean(beanName, mbd);
}
6.2.1 获取Class对象
在Spring 启动过程中,bean 的实例化用反射机制完成。 反射机制的运行依赖于Class 对象。
在createBeanInstance代码中, 获取Class 对象有两种选择,
- Class.forName
- ClassLoader.loadClass
6.2.2 instantiateBean-无参构造函数实例化bean
比较简单,总体流程遵循常见的反射讲解中,最简单的那种demo1
2constructorToUse = clazz.getDeclaredConstructor();
ctor.newInstance(argsWithDefaultValues);
6.2.3 autowireConstructor-构造函数注入实例化bean
autowireConstructor
有可能会遇到循环依赖。 构造函数注入方式中,bean 必须已经实例化完成,因此三层缓存机制中提前暴露的早期引用是如何解决构造函数注入引发的循环依赖的。
6.3 earlySingletonExposure
bean 已经完成了初步 实例化, 虽然还不完美(还没有进行 第二步populate和第三步 init),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以 Spring 此时将这个对象提前曝光出来让大家认识,让大家使用。
提前暴露引用的逻辑,是添加第3层缓存,
1 | addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); |
ObjectFactory 的匿名类实现是getEarlyBeanReference, getEarlyBeanReference
方法中处理了AOP 代理的情况,bean 需要被AOP 代码,会返回代理对象的引用。
6.4 doCreateBean- populateBean
在属性填充阶段,也有可能会遇到循环依赖。这一阶段的循环依赖可以通过三层缓存提前暴露早期引用解决。
6.5 doCreateBean- initializeBean
只留重点代码,bean 的init 流程如下
1 | protected Object initializeBean(String beanName, Object bean, { RootBeanDefinition mbd) |
init 过程中,涉及到多个拓展点,如 Aware、BeanPostProcessor,具体关于拓展点的介绍,可以阅读Spring 启动过程拓展点拓展点
6.5.1 Aware-invokeAwareMethods
点击查看invokeAwareMethods, 其逻辑涉及到3个Aware 接口。
Spring框架中,Aware
接口的作用是让bean能够拿到对Spring容器中的各种资源,从而增强bean的功能和灵活性。
不同的 Aware 基本都能够见名知意,Aware之前的名字就是可以拿到什么资源,例如BeanNameAware
可以拿到BeanName,以此类推。调用时机需要注意:所有的Aware方法都是在init阶段之前调用的
6.5.2 BeanPostProcessor
applyBeanPostProcessorsBeforeInitialization
和applyBeanPostProcessorsAfterInitialization
是BeanPostProcessor
的接口实现, 分别在init 前后调用,可以修改bean 实例, Spring AOP 就是基于BeanPostProcessor
实现的
1 | public interface BeanPostProcessor { |
6.5.3 invokeInitMethods - InitializingBean
1 | protected void invokeInitMethods(String beanName, Object bean, { RootBeanDefinition mbd) |
InitializingBean
是Spring框架中定义的一个接口,用于在bean的所有属性设置完成后执行自定义的init 逻辑。它是Spring生命周期回调机制的一部分,使得bean可以在属性注入完成后但在使用之前执行一些自定义的初始化工作。1
2
3public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
实现InitializingBean
接口的bean可以通过afterPropertiesSet
方法在Spring容器完成依赖注入后但在使用bean之前执行一些初始化逻辑。典型的使用场景包括:
- 检查依赖属性:确保所有必需的属性已经被设置,并且满足某些业务约束。
- 执行初始化操作:例如,建立数据库连接、加载配置文件、启动辅助服务等。
- 其他初始化逻辑:在bean被使用之前需要完成的任何其他准备工作。
@PostConstruct
注解
除了实现InitializingBean
接口,Spring还提供了一个更常用的注解@PostConstruct
,可以用于同样的初始化目的。@PostConstruct
是Java EE的标准注解,通常更简洁并且更符合注解驱动开发的风格。
使用@PostConstruct
注解的示例:
1 | import javax.annotation.PostConstruct; |
InitializingBean
与@PostConstruct
的区别
- 实现方式:
InitializingBean
:通过实现接口的方法来定义初始化逻辑。@PostConstruct
:通过在方法上使用注解来定义初始化逻辑。
- 使用场景:
InitializingBean
:更适用于需要显式实现接口的场景,通常在框架或底层代码中使用。@PostConstruct
:更适用于应用层代码,更加简洁和直观。
- 灵活性:
这里的init 指的是在 XML 配置或 Java 配置中指定的自定义 init-method
方法。在 Spring 框架中,当一个 Bean 的生命周期到达初始化阶段时,如果该 Bean 配置了自定义的初始化方法,Spring 将会调用这个方法来进行一些初始化操作。
在 XML 配置中,可以通过 init-method
属性指定一个 Bean 的自定义初始化方法。例如:1
2
3<bean id="exampleBean" class="com.example.MyBean" init-method="customInit">
<!-- Bean 属性配置 -->
</bean>
在 Java 配置中,可以使用 @Bean
注解的 initMethod
属性来指定自定义初始化方法。例如:1
2
3
4
5
6
7
8
public class AppConfig {
public MyBean exampleBean() {
return new MyBean();
}
}
invokeCustomInitMethod
方法的作用就是在 Bean 初始化过程中,调用在 XML 或 Java 配置中指定的自定义初始化方法。
6.5.5 拓展点执行顺序
以上内容中提到的拓展点,其执行顺序在代码中已经有比较清晰的展示,这里再强调一下
- Aware
- BeanPostProcessor
的
postProcessBeforeInitialization`方法。如果有多个BeanPostProcessor,会按顺序调用它们的postProcessBeforeInitialization方法。(其实这个步骤中也会有相关Aware) - 使用
@PostConstruct
注解的方法。此时,所有依赖已经注入完毕,Bean已经完全配置好,但还没有执行任何自定义的初始化方法。 - 实现
InitializingBean
接口的afterPropertiesSet
方法。 - 在XML配置或Java配置中指定的自定义
init-method
方法。 BeanPostProcessor
的postProcessAfterInitialization
方法。如果有多个BeanPostProcessor,会按顺序调用它们的postProcessAfterInitialization方法
6.6 缓存管理-addSingleton
当通singletonObject = singletonFactory.getObject()
获得到bean 后,需要重新管理3层缓存中的数据
1 | // init完成的bean, 添加到一级缓存,并从二三级缓存中删除 |
7. 再看getBean-从2种视角出发
在第4小节doGetBean 的注释中受了,对于一个bean 触发getBean 的逻辑有2中情况
- bean 的正常创建流程
- 依赖注入触发的getBean
再看看下getSinngleton 的代码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代码均在 DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
// 首先从 singletonObjects(单例缓存)中快速获取 Bean 实例,如果存在则直接返回。
Object singletonObject = this.singletonObjects.get(beanName);
// 如果初步检查没有找到实例,并且该单例当前正在创建中,则从 earlySingletonObjects(早期单例缓存)中查找。
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//如果仍未找到实例,并且允许早期引用,则进入同步块。
//在同步块内,再次从 singletonObjects 和 earlySingletonObjects 中检查实例。
// 这是为了避免在进入同步块的过程中,有其他线程已经创建了该单例实例。
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//如果仍未找到实例,则尝试从 singletonFactories 中获取单例工厂,
// 并singletonFactory创建早期引用,将其放入 earlySingletonObjects 中,并从 singletonFactories 中移除。
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
- 在正常的创建流程中,
getSingleton(beanName, true)
返回的通常是null
,因为此时Bean尚未创建完毕。 - 在依赖注入的场景中,通过
getSingleton(beanName, true)
可以通过3级缓存获取到二级缓存 早期引用,从而解决循环依赖问题。
可以看出在一个bean 的正常创建流程中, 只会触发对一级、三级缓存的操作, 并不会主动生成早期引用, 只有在循环依赖的情况下,才会触发对2级缓存。
8. getObjectForBeanInstance
现在不论是正常的创建流程需要createBean,还是依赖注入可以直接获取早期引用,获取到sharedInstance 都没有直接返回,而是经过getObjectForBeanInstance 处理后才返回。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
30AbstractBeanFactory.java
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
// 省略了非常多代码,只保留了相关逻辑
protected <T> T doGetBean(
String name, boolean typeCheckOnly) Class<T> requiredType, Object[] args,
throws BeansException {
// 尝试从缓存中获取
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 缓存中存在则直接返回
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}else{
// 缓存中没有则需要创建并回写缓存
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
return bean;
}
在 Spring 框架中,getObjectForBeanInstance
方法用于处理FactoryBean 这种特殊的bean ,以确保返回给用户的是正确类型和状态的对象。
关于理解 Spring FacrotyBean
简单来讲,FactoryBean 也是一个bean , 但是通过它可以生产出其他bean。 FactoryBean有两种用法
- 使用
getBean("&beanName")
可以获取FactoryBean
实例本身,而不是FactoryBean
创建的对象。 - 使用
getBean("beanName")
则获取由FactoryBean
创建的对象。
getObjectForBeanInstance
的存在就是为了处理如果一个bean 是FactoryBean,那到底是返回FactoryBean,还是返回FactoryBean 创建的对象
1 | protected Object getObjectForBeanInstance( |
9. 三级缓存的转换流程
- 一个非懒加载的单例bean 实例化过程中, 只会主动去添加一级、三级缓存,只有循环依赖才会触发二级缓存
- 对于一个bean ,不论是完整引用,还是早期引用,还是生成早期引用的ObjectFactory,在同一时刻, 只会存在于一级缓存中
9.1 为什么需要第三级缓存-addSingletonFactory
doCreateBean->addSingletonFactory, 在create 步骤后, populate 步骤前
第三级缓存的存在主要是为了处理代理机制。通常情况下,代理对象会在Bean的init阶段生成。如果在解决循环依赖时提前暴露的引用不是代理对象的引用,而是原始对象的引用,即doCreateBean代码中 bean 和exposedObject 不一致1
2
3
4
5
6BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
Object bean = instanceWrapper.getWrappedInstance();
populateBean(beanName, mbd, instanceWrapper);
Object exposedObject =initializeBean(beanName, exposedObject, mbd)
但是如果可以在循环依赖的处理中处理中就为bean 生成代理对象的引用(如果需要代理的话), 那么就可以和init阶段产生的代理引用保持一致了。这个功能是通过getEarlyBeanReference 来完成的
如果是需要代理的对象,getEarlyBeanReference就会生成其代理对象的引用,而不是原对象的引用。 在解决循环依赖提前暴露引用时,如果提前暴露的引用不是其代理对象的引用的话,就会产生数据不一致的情况。
1 | addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); |
这里又有一个ObjectFactory的匿名实现类, 且添加进了三级缓存singletonFactories中, 下面来具体看下getEarlyBeanReference实现了什么
getEarlyBeanReference
由此可见,三级缓存用于生产二级缓存中存放的早期引用,如果是需要代理的对象,getEarlyBeanReference就会生成其代理对象的引用,而不是原对象的引用。 之所以需要三级缓存是因为代理机制的存在, 在整个bean 的生命周期中,会在init 阶段生成其代理对象(如果需要的话),那么在解决循环依赖提前暴露引用时,如果提前暴露的引用不是其代理对象的引用的话,就会产生数据不一致的情况
1 | protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { |
9.2 二级缓存
1 | protected Object getSingleton(String beanName, boolean allowEarlyReference) { |
9.3 一级缓存
getSingleton -> addSingleton,
在bean的实例化完成后, 会将其引用放入一级缓存,同时删除2、3级缓存1
2
3
4
5
6
7
8protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
10. 循环依赖
在Spring IOC容器 和 Spring bean讲过,Bean 依赖关系声明方式有3种
循环依赖的产生基于依赖关系的3种声明方式,也可以分为3种
- A 的构造方法中依赖了 B 的实例对象,同时 B 的构造方法中依赖了 A 的实例对象
- A 的构造方法中依赖了 B 的实例对象,同时 B 的某个 field 或者 setter 需要 A 的实例对象,以及反之
- A 的某个 field 或者 setter 依赖了 B 的实例对象,同时 B 的某个 field 或者 setter 依赖了 A 的实例对象,以及反之
Spring 中 非懒加载的单例bean, 初始化过程可以分为3个步骤,循环依赖的步骤集中在第一步和第二步:
- createBeanInstance, 通过构造函数创建实例
- populateBean,填充属性/依赖注入
- initializeBean,初始化
10.2 循环依赖的解决-三层缓存、提前暴露引用
Spring 循环依赖的理论依据其实是 Java 基于引用传递,当我们获取到对象的引用时,对象的 field 或者或属性是可以延后设置的。
三层缓存中的二、三级缓存涉及用于提前暴露引用,以此解决循环依赖问题。
关于三级缓存,具体可以看上面的内容
10.3 循环依赖的解决前提
在3级缓存的机制下, 以上三种情况只能解决2&3,1无法解决
因为Spring容器能顺利实例化以构造函数注入方式配置的Bean有一个前提:Bean构造函数入参引用的对象必须已经准备就绪,即需要完成populate和init 这两个步骤,不能只有一个引用,因此提前曝光也没用。
由于这个机制的限制,如果两个Bean都采用构造函数注入,而且都通过构造函数入参引用对方,就会发生类似于线程死锁的循环依赖问题。将构造函数注入方式调整为属性注入方式就可以了。