Spring Core Container 源码分析三:Spring Beans 初始化流程分析

Spring源码分析-Spring核心类认识(一) 相信每个Java程序员都使用过Spring这个框架,Spring不仅作用于服务器端的开发,从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益,如今的Spring全家桶已经是风靡全球,深受广大程序员的喜爱,本系列文章将带你一步步窥探Spring核心。 阅读详情

前言

本文是笔者所著的 Spring Core Container 源码分析系列之一;

本篇文章主要试图梳理出 Spring Beans 的初始化主流程和相关核心代码逻辑;

本文转载自本人的博客,伤神的博客 http://www.shangyang.me/2017/04/01/spring-core-container-sourcecode-analysis-beans-instantiating-process/

本文为作者的原创作品,转载需注明出处;

源码分析环境搭建

参考 Spring Core Container 源码分析二:环境准备

测试用例

依然使用这个官网上的用例,来进行调试;

Person.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

    
package org.shangyang.spring.container;
/**
-
- @author shangyang
*
*/
public class Person {
String name;
Person spouse;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person getSpouse() {
return spouse;
}
public void setSpouse(Person spouse) {
this.spouse = spouse;
}
}

beans.xml


    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

    
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xmlns:p= "http://www.springframework.org/schema/p"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 传统的方式 -->
<bean name="john" class="org.shangyang.spring.container.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
<bean name="jane" class="org.shangyang.spring.container.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>


    
1
2
3
4
5
6
7
8
9
10
11
12
13

    
@Test
public void testApplicationContext(){
@SuppressWarnings( "resource")
ApplicationContext context = new ClassPathXmlApplicationContext( "beans.xml");
Person p = context.getBean( "john", Person.class);
assertEquals( "John Doe", p.getName() );
assertEquals( "Jane Doe", p.getSpouse().getName() );
}

源码分析

备注,这里只针对 Spring 容器实例化 singleton bean 的主流程进行介绍;singleton bean 在 Spring 容器中被初始化的特点是,在 Spring 容器的启动过程中就进行初始化;

(最好的分析源码的方式,就是通过高屋建瓴,逐个击破的方式;首先通过流程图获得它的蓝图(顶层设计图),然后再根据蓝图上的点逐个击破;最后才能达到融会贯通,胸有成竹的境界;所以,这里作者用这样的方式带你深入剖析 Spring 容器里面的核心点,以及相关主流程到底是如何运作的。)

主流程

bean-creation-overall-process.pngbean-creation-overall-process.png

本章节我们将详细去阐述的是,Spring 容器是如何对 Singleton bean 进行初始化并注册到当前容器的;与之相关的主要有两个流程,

  1. 解析 bean definitions 并注册
    解析 bean definitiions 并注册到当前的 BeanFactory 中;此步骤是在 step 1.1.1.2 obtainFreshBeanFactory 完成;更详细的介绍参考解析并注册 bean definitions 流程

  2. 从 #1 中找到所有的已注册的 singleton bean definitions,遍历,实例化得到 Singleton beans;此步骤对应的是 step 1.1.1.11 finishBeanFactoryInitialization 开始进行 singleton bean 的构造过程,其后调用AbstractBeanFactory#getBean(beanFactory)方法进行构造;更详细的介绍参考 Do Get Bean 流程。

解析并注册 bean definitions 流程

该部分参考新的博文 Spring Core Container 源码分析七:注册 Bean Definitions

Do Get Bean 流程

Do Get Bean 流程的入口是 AbstractBeanFactory#doGetBean 方法,主流程图如下,

do-get-bean-process.png do-get-bean-process.png

主流程大致为,从缓存中找到是否已经实例化了该 singleton bean,如果已经实例化好了,那么就直接返回;如果在缓存中没有找到,则将当前的 bean 封装为 RootBeanDefinition,然后通过调用 DefaultSingletonBeanRegistry#getSingleton 得到初始化好的 singleton bean,然后将其注册至缓存( step 1.3.3 addSingleton ),然后再判断是普通 bean 还是 factory bean 作必要的处理( step 1.4 getObjectForBeanInstance )后,最后返回;

RootBeanDefinition

初始化了一个 RootBeanDefinition 对象,正如其类名描述的那样,是该 bean 的顶层描述;包含了 bean 的字段属性,ref属性以及继承相关等等属性;

Step 1.3:DefaultSingletonBeanRegistry#getSingleton

此步骤的相关代码如下,


    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

    
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

调用父类 DefaultSingletonBeanRegistry#getSingleton 方法;注意,这里通过接口 ObjectFactory<Object> 定义了一个回调方法 getObject(),通过该回调方法调用 AbstractAutowireCapableBeanFactory#createBean 方法,通过此回调方法正式拉开了实例化 bean 的序幕。

Step 1.3.1.1:AbstractAutowireCapableBeanFactory#createBean

AbstractAutowireCapableBeanFactory#doCreateBean 方法是初始化 bean 的最核心的入口方法,执行流程如 Do Get Bean 流程所示,

  1. 主流程主要做了这么三件事情,一、instantiate bean;二、populate bean;三、initialize bean;

  2. 包含五个子流程,他们分别是 factory instantiate bean、autwire instantiate bean、default instantiate bean、populate bean 以及 initialize bean,其中,前三个子流程对应第一件事情,实例化 bean;其次的子流程 populate bean 对应的是第二件事情,为 bean 设置 property 参数;最后一个子流程 [initialize bean] 对应最后一件事情既初始化 bean,这里的初始化指的是是对创建好的 bean 做一些修饰动作的。

下面我们分别来分析这三件事情

第一件事情:instantiate bean

该步骤对应 Do Get Bean 流程中的 Step 1.3.1.1.3.1 createBeanInstance;根据 bean 的不同配置方式,实现了三种实例化 bean 的方式,分别是 factory instantiate bean、autwire instantiate bean 以及 default instantiate bean;

factory instantiate bean

用工厂方法实例化 bean,待叙;

autwire instantiate bean

通过 autowire 注解的方式实例化 bean,待叙;

default instantiate bean

此步骤对应 Do Get Bean 流程中的 Step 1.3.1.1.3.1.3 instantiateBean,其对应子流程 default instantiate bean 如下图所示,

default-instantiate-bean-process.png default-instantiate-bean-process.png

注意几点

  • 这里仅仅是通过使用其构造函数 constructor 使用Java 反射实例化了 bean,并没有对其进行任何的参数赋值,赋值过程参考 populate bean 流程;
  • 将 bean 封装为 BeanWrapper,然后
    • 注册 default PropertyEditor
    • 注册 custom PropertyEditor

最后返回 BeanWrapper

第二件事情:populate bean

对应 Do Get Bean 流程中的 Step 1.3.1.1.3.4 populateBean,通过调用 AbstractAutowireCapableBeanFactory#populateBean 正式给 bean 的参数进行赋值;为什么方法名取名为populate呢?查百度翻译,结果是居住于、生活于、移民于、落户于的意思,丝毫没有赋值的意思,但是记得以前和美国人共事的时候,他们非常喜欢用这个词populate,特别是在创建某个对象的时候,他们特别喜欢用这个词;后来无意中发现,populate有创建和填充的意思,所以,这里的意思就是,填充这个 bean;下面看看 populate bean 的流程图,看看它是怎么玩的,

populate-bean-process.png populate-bean-process.png

通过调用 AbstractAutowireCapableBeanFactory#applyPropertyValues 方法正式给 bean 进行赋值操作,赋值的主流程主要包括两个部分,resolve property value object 和 set property value to bean

resolve property value

对应上面 Sequence Diagram 中的 Step 1.5 - Step 1.7;这一步主要是去遍历当前 bean 所有的 property,并依次解析(resolve)得到对应的 Java 对象;通过方法 BeanDefinitionValueResolver#resolveValueIfNecessary 进行解析,解析的过程是针对不同类型的 Property,采用不同的解析方式,里面目前总共对应了十种类型,先看看它的源码,


    
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

    
public Object resolveValueIfNecessary(Object argName, Object value) {
// We must check each value to see whether it requires a runtime reference
// to another bean to be resolved.
if (value instanceof RuntimeBeanReference) { // 这里表示该 bean definition ref 引用的是一个 bean,那么这里,必须对该 bean 进行初始化操作;
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
else if (value instanceof RuntimeBeanNameReference) {
String refName = ((RuntimeBeanNameReference) value).getBeanName();
refName = String.valueOf(doEvaluate(refName));
if (! this.beanFactory.containsBean(refName)) {
throw new BeanDefinitionStoreException(
"Invalid bean name '" + refName + "' in bean reference for " + argName);
}
return refName;
}
else if (value instanceof BeanDefinitionHolder) {
// Resolve BeanDefinitionHolder: contains BeanDefinition with name and aliases.
BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
}
else if (value instanceof BeanDefinition) {
// Resolve plain BeanDefinition, without contained name: use dummy name.
BeanDefinition bd = (BeanDefinition) value;
String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +
ObjectUtils.getIdentityHexString(bd);
return resolveInnerBean(argName, innerBeanName, bd);
}
else if (value instanceof ManagedArray) {
// May need to resolve contained runtime references.
ManagedArray array = (ManagedArray) value;
Class<?> elementType = array.resolvedElementType;
if (elementType == null) {
String elementTypeName = array.getElementTypeName();
if (StringUtils.hasText(elementTypeName)) {
try {
elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
array.resolvedElementType = elementType;
}
catch (Throwable ex) {
// Improve the message by showing the context.
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Error resolving array type for " + argName, ex);
}
}
else {
elementType = Object.class;
}
}
return resolveManagedArray(argName, (List<?>) value, elementType);
}
else if (value instanceof ManagedList) {
// May need to resolve contained runtime references.
return resolveManagedList(argName, (List<?>) value);
}
else if (value instanceof ManagedSet) {
// May need to resolve contained runtime references.
return resolveManagedSet(argName, (Set<?>) value);
}
else if (value instanceof ManagedMap) {
// May need to resolve contained runtime references.
return resolveManagedMap(argName, (Map<?, ?>) value);
}
else if (value instanceof ManagedProperties) {
Properties original = (Properties) value;
Properties copy = new Properties();
for (Map.Entry<Object, Object> propEntry : original.entrySet()) {
Object propKey = propEntry.getKey();
Object propValue = propEntry.getValue();
if (propKey instanceof TypedStringValue) {
propKey = evaluate((TypedStringValue) propKey);
}
if (propValue instanceof TypedStringValue) {
propValue = evaluate((TypedStringValue) propValue);
}
copy.put(propKey, propValue);
}
return copy;
}
else if (value instanceof TypedStringValue) {
// Convert value to target type here.
TypedStringValue typedStringValue = (TypedStringValue) value;
Object valueObject = evaluate(typedStringValue);
try {
Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
if (resolvedTargetType != null) {
return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
}
else {
return valueObject;
}
}
catch (Throwable ex) {
// Improve the message by showing the context.
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Error converting typed String value for " + argName, ex);
}
}
else {
return evaluate(value);
}
}

这里主要关注两类解析,

RuntimeBeanReference 类型解析

对应需要被解析的配置为john的 property spouse,它所对应的值是另一个 bean jane;既是一个去解析一个ref bean。


    
1

    
<property name="spouse" ref="jane"/>

所以可以看到,源码中是通过 resolveReference 方法调用 AbstractBeanFactory#getBean 方法去实例化一个 bean jane 并返回;对应的源码如下,
BeanDefinitionValueResolver#resolveReference


    
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

    
/**
* Resolve a reference to another bean in the factory.
*/
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
try {
String refName = ref.getBeanName();
refName = String.valueOf(doEvaluate(refName));
if (ref.isToParent()) {
if ( this.beanFactory.getParentBeanFactory() == null) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Can't resolve reference to bean '" + refName +
"' in parent factory: no parent factory available");
}
return this.beanFactory.getParentBeanFactory().getBean(refName);
}
else {
Object bean = this.beanFactory.getBean(refName); // 去得到这个 ref bean
this.beanFactory.registerDependentBean(refName, this.beanName); // 注册
return bean;
}
}
catch (BeansException ex) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName, ex);
}
}

通过调用this.beanFactory.getBean(refName)再次进入Do Get Bean 流程初始化得到该 ref bean;

最后,将解析出来的 Property value 放在一个deepCopy的 ArrayList 列表对象中;顾名思义,就是对值进行了一次深度的拷贝,然后将其作为 bean 的参数,随后开始进行赋值操作);

TypedStringValue 类型解析

这里主要被解析的 Property value 的类型配置为


    
1

    
<property name="name" value="John Doe"/>

其 Property value 的值在配置文件中就是一个纯的字符串类型;但从源码中可以知道,Property value 是可以包含 value type 的,所以,在解析 TypedStringValue 的时候,需要根据 value type 进行解析;具体逻辑参考resolveTargetType(typedStringValue);方法。

set property value to bean

此步骤的主流程主要是通过Java Method 反射将解析出来的值赋值给当前的 bean;对应时序图中的 Step 1.8 setPropertyValues

可以看到,通过遍历 deepCopy ArrayList 对象中已经解析过后的 PropertyValue,最终由 BeanWrapperImpl 对象通过方法的反射,将值注入给当前的 bean,Step 1.8.1.2.1.2.2 writeMethod.invoke

BeanWrapperImpl.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

    
@Override
public void setValue(final Object object, Object valueToApply) throws Exception{
final Method writeMethod = ( this.pd instanceof GenericTypeAwarePropertyDescriptor ?
((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
this.pd.getWriteMethod());
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged( new PrivilegedAction<Object>() {
@Override
public Object run() {
writeMethod.setAccessible( true);
return null;
}
});
}
else {
writeMethod.setAccessible( true);
}
}
....
writeMethod.invoke(getWrappedInstance(), valueToApply);
....
}

通过这一步,将相关的 property populate 给 bean 以后,才算 bean 的实例化完成;

第三件事情:initialize bean

正如流程图中所描述的那样,这个步骤的名字非常然人迷惑,我将流程图中写的备注摘录如下,

“注意, 这里的名称很容易让人产生混淆, 很容易让人产生这样一个疑问, 前面不是已经初始化好了, bean 创建好了, bean 相关的 property 也设置好了, 怎么这里还要进行初始化? 这里其实恰恰充分体现了西方人写代码的严谨, 前面的部分叫做 instantiation, 叫做”实例化”, 而这里叫做 initialization, 叫做 “初始化”; 这样一说, 差不多就明白了, “实例化”就是从 class 得到 instance 的过程; 而”初始化”, 包含的意义更广泛, 其意义包含了”实例化”和其它对 instance 的修饰的过程, 而这里, 其实就是对已经创建好的 bean instance 进行”修饰”的过程。”

所以,这里的 initialize 其实就是对通过 instantiate bean 和 populate bean 两个步骤实例化好的 bean 进行后续必要的修饰;我们通过流程图来看看,它是怎么去修饰的,
initialize-bean-process.pnginitialize-bean-process.png

通过 AbstractAutowireCapableBeanFactory#initializeBean 方法进行对 bean 的修饰过程,看源码,(删除了大部分不相关的代码)

AbstractAutowireCapableBeanFactory.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

    
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
invokeAwareMethods(beanName, bean); // 调用实现了 *Aware 接口的方法,比如注入 ApplicationContext...
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 调用 bean-post-processor 的 before initialization 回调方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd); // 调用 InitializingBean#afterPropertiesSet 回调
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// 调用 bean-post-processor 的 after initialization 回调方法
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean; // 这里的 wrapped bean 指的是被 bean-post-processor 修饰以后的包装 bean
}

整个过程可以理解为三大块,

下面,我们依次来梳理这三个部分;

注入 Aware 对象

对应流程图中的 Step 1.1 invokeAwareMethods,该步骤注册所有实现了Aware接口的 beans

Aware.java


    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

    
/**
* Marker superinterface indicating that a bean is eligible to be
* notified by the Spring container of a particular framework object
* through a callback-style method. Actual method signature is
* determined by individual subinterfaces, but should typically
* consist of just one void-returning method that accepts a single
* argument.
*
* <p>Note that merely implementing { @link Aware} provides no default
* functionality. Rather, processing must be done explicitly, for example
* in a { @link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessor}.
* Refer to { @link org.springframework.context.support.ApplicationContextAwareProcessor}
* and { @link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory}
* for examples of processing { @code *Aware} interface callbacks.
*
* @author Chris Beams
* @since 3.1
*/
public interface Aware {
}

从 Aware 接口的注释中可以看到,允许实现了该 Aware 接口的当前 bean 能够有机会通过回调的方式注入 Spring 容器中默认实现了 Aware 接口的 bean,比如 BeanFactory 等;看看源码,

AbstractAutowireCapableBeanFactory.java


    
1
2
3
4
5
6
7
8
9
10
11
12
13

    
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory. this);
}
}
}

可以看到,注入了三种情况,

  1. 当当前 bean 实现了 BeanNameAware 接口,只是调用了 setBeanName;给我目前的感觉是,没有起到什么大的作用,把自己的 beanName 设置给自己,有什么用处?嗯,倒是想到一个,logging;

  2. 当当前 bean 实现了 BeanClassLoaderAware 接口,将 Spring 容器的 BeanClassLoader 注入到当前 bean;记住,如果想要获得加载 Bean 当前的 ClassLoader 对象的时候,只需要让 Bean 实现 BeanClassLoaderAware 接口并实现相应接口方法即可。

  3. 当当前 bean 实现了 BeanFactoryAware 接口,将 Spring 容器中与 bean 初始化相关的 BeanFactory 实例(这里对应的是 DefaultListableBeanFactory 实例)注册给当前的 bean。 

当梳理完这个部分以后,我相信读者会和我一样有这样的疑问,我们不是经常通过Aware的方式注入ApplicaitonContext对象的吗?但和明显,上述的代码并没有注入ApplicationContext对象呀?看如下部分分析,

下列部分是延生部分

但是要特别特别注意的是,这里并没有注入ApplicationContext对象,要注入ApplicaitonContext对象,bean 必须实现ApplicatonContextAware接口;

ApplicationContextAware.java


    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

    
public interface ApplicationContextAware extends Aware {
/**
* Set the ApplicationContext that this object runs in.
* Normally this call will be used to initialize the object.
* <p>Invoked after population of normal bean properties but before an init callback such
* as { @link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* or a custom init-method. Invoked after { @link ResourceLoaderAware#setResourceLoader},
* { @link ApplicationEventPublisherAware#setApplicationEventPublisher} and
* { @link MessageSourceAware}, if applicable.
* @param applicationContext the ApplicationContext object to be used by this object
* @throws ApplicationContextException in case of context initialization errors
* @throws BeansException if thrown by application context methods
* @see org.springframework.beans.factory.BeanInitializationException
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

从注解中可以看到,它明确标注,是在 populate bean 以后,在 处理 InitializingBean 接口回调 之前调用,那是到底是在什么时刻呢?我们查看下方法 setApplicationContext 方法在 workspace 中的引用,它是在 ApplicationContextAwareProcessor#invokeAwareInterfaces 方法中被调用,而 ApplicationContextAwareProcessor 正好是一个 BeanPostProcessor 对象,所以,ApplicaitonContext对象实际上是在 bean-post-procesor before initialization流程中被注入的,看源码,

ApplicationContextAwareProcessor.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

    
class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ConfigurableApplicationContext applicationContext;
private final StringValueResolver embeddedValueResolver;
/**
* Create a new ApplicationContextAwareProcessor for the given context.
*/
public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
}
@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
....
invokeAwareInterfaces(bean);
....
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment( this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver( this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader( this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher( this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource( this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext( this.applicationContext);
}
}
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}

回调 bean-post-processors 接口方法

先来看看BeanPostProcessor接口的源码,


    
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

    
public interface BeanPostProcessor {
/**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's { @code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if { @code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's { @code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding { @code bean instanceof FactoryBean} checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* { @link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if { @code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

该接口定义了两个回调方法,

  • Object postProcessBeforeInitialization(Object bean, String beanName)
    从方法的注释中不难发现,该方法是在回调 InitializeBean 接口方法之前调用,并且是在 populate bean) 之后进行的调用,通常是对原有 bean 做一层封装,然后返回该封装对象;这就是我在前文所描述的,其实就是是对原有 bean 起到修饰的作用;

  • Object postProcessAfterInitialization(Object bean, String beanName)
    从方法的注释中不难发现,该方法是在回调 InitializeBean 接口方法之前调用,同样也是在 populate bean) 之后进行的调用

所以,bean-post-processors 总共有两次调用的时机,分别是 before initialization 和 after initialization,而且要特别注意的是,该回调方法是针对对容器中所有的普通 bean 进行的回调;

before initialization

该步骤对应 Step 1.2 applyBeanPostProcessorsBeforeInitialization,然后正式调用 Step 1.2.1 beanPostProcessor.postProcessBeforeInitialization bean-post-processor 的回调方法,不过这里要注意的是,如果 Step 1.2.1 返回 null,会终止余下的 post-bean-processor 的执行,并且直接返回一个 null,该 null 同样被视为是封装后的产物,既然是 null,那么后续的 bean-post-processor 也无需处理了,也无法对原有的 bean 进行包装了.. 不过,我到觉得这里的 Spring 源码的逻辑应该改进一下,不允许返回 null 的情况,如果返回 null 则报错最好,因为 bean-post-processor 是对原有 bean 进行修饰,是新增属性,而非干掉实例本身.. 来看看源码,

AbstractAutowireCapableBeanFactory.java


    
1
2
3
4
5
6
7
8
9
10
11
12
13

    
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}

如果某个 beanProcessor 处理返回一个 null 则直接返回,并且终止其余的 bean-post-processors

after initialization

对应的是 step 1.4 applyBeanPostProcessorsAfterInitialization 步骤,里面的逻辑和 before initialization 逻辑类似,不再赘述。

回调 InitializingBean 接口方法

该步骤对应的是 Step 1.3: invokeInitMethod,回调 InitializingBean 接口方法,看源码,

AbstractAutowireCapableBeanFactory.java (删除了大部分不相关的代码)


    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

    
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod( "afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug( "Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
.....
((InitializingBean) bean).afterPropertiesSet();
.....
}
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}

可以看到,主要回调的是InitializingBean接口的afterPropertiesSet方法,所以,我们可以让某个 bean 实现 InitializingBean 接口,并通过该接口实现一些当 bean 实例化好以后的回调方法,注意afterPropertiesSet并不返回任何值,所以,这里不是像 bean-post-processor 那样对 bean 起到修饰的作用,而是起到纯粹的调用作用;

总结(我的思考)

以上便是 Spring IoC 容器的核心实现了,容器要达到的最本质的目的就是IoC既反转控制,由容器来负责管理对象实例相关依赖的初始化和注入,而不再是由 bean 自己去控制自己的依赖的初始化和注入过程了;而IoC的实现是通过DI既是依赖注入实现的;也就是为什么上面所介绍的内容都是围绕着 Spring 容器如何实例化一个 bean,如何赋值,如何进行回调等一系列在 bean 的构建声明周期过程中的种种行为;

这样做能达到的好处就是,

  1. 实现了对象的依赖属性和依赖对象的高度可定制化,我们可以通过容器配置的方式,轻松的修改 bean 所的依赖对象,以改变原有的其行为;

    (这里我突然想到“微内核架构”,Java 的一种内置的依赖注入的方式,通过在 META-INF 中配置接口属性文件,里面其实就是配置的一个接口的实现类,通过它来控制接口在 JVM 中该接口所使用的实现类;但它只是控制某个接口如何在 JVM 中实现,将接口的实现翻转控制了,而 Spring 是对所有的 Class Instance 进行翻转控制,所以一个是站在局部上的反转控制设计,一个是站在全局上的反转控制设计,不可同日而语!)

  2. 通过 bean-post-processor 扩展点能够对原始的 bean 进行包装,通过这样的方式,我们可以包装得到一个 proxy bean,提供相关的 AOP 操作,比如提供事务,异常处理等等方法注入的实现;

Spring框架面试核心要点与实战解析 控制反转(IoC)和依赖注入(DI)是Spring框架的核心设计理念,通过BeanFactory和ApplicationContext等容器实现对象生命周期管理。面向切面编程(AOP)则采用代理模式实现横切关注点的模块化,支持JDK动态代理和CGLIB两种方式。这些机制共同构成了Spring的基石,广泛应用于企业级应用开发。Spring Boot通过自动配置和starter机制进一步简化了开发流程,其启动过程涉及环境准备、上下文刷新等关键阶段。在微服务架构中,Spring Cloud提供服务发现、负载均衡等 阅读详情

相关推荐

java框架Spring01:核心容器模块(Core Container)

Spring010. Spring框架概述1. Spring IOC(Inverse Of Controll)0. Inverse Of Controll(控制反转)1. 依赖注入方式1.1. 依赖注入(DI)(个人理解)1. 什么是依赖?1.2 Spring IOC 中的依赖注入1.xml配置注入2.注解注入2. IoC 容器(对象工厂)3. 总结 0. Spring框架概述 Spring 是⼀种轻量级开发框架,旨在提⾼开发⼈员的开发效率以及系统的可维护性。 我们⼀般说 Spring 框架指的都是 Spr

qq_37442823的博客 1016

spring源码解析---spring-core(一)

https://github.com/seaswalker https://github.com/seaswalker/Spring 使用版本spring4.2.2或者4.1.1 基本 本部分从最基本的Spring开始。配置文件: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" ...

程序员ken的博客 2168

Spring Core核心知识介绍

Spring Core核心知识介绍,Spring常用注解,依赖注入,事务管理等

spring源码分析之spring-core总结篇

1.spring-core概览 spring-core是spring框架的基石,它为spring框架提供了基础的支持。 spring-core从源码上看,分为6个package,分别是asm,cglib,core,lang,objenesis和util。 1.1 asm 关于asm的内幕参见博客: spring源码分析之spring-core asm概述 1.2 cgli...

1125

spring源码解析思维导图

XmlBeanFactory继承自DefaultListableBeanFactory,而DefaultListableBeanFactory是整个bean加载的核心部分,是Spring注册及加载bean的默认实现,XmlBeanFactory与DefaultListableBeanFactory不同的地方其实是在XmlBeanFactory中使用了自定义的XML读取器XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取。

为什么我把「大模型Clouder认证:基于百炼平台构建智能体应用」作为大模型学习的第一站?从 0 到拿证全流程经验分享

【摘要】作为一个刚拿下「大模型 Clouder 认证:基于百炼平台构建智能体应用」的过来人,想在开头就和大家聊聊这个认证的特殊意义 —— 在企业加速 AI 落地的今天,能用低代码快速构建智能体应用,已经成为 AI 从业者的核心竞争力。无论是企业微信客服提效、钉钉机器人自动化办公,还是网站智能助手升级,这些场景化能力正是当下数字化转型的刚需。

davidkorhenn的博客 1526

spring源码解析-容器的基本实现

spring源码解析-容器的基本实现,本人精心整理,包含详细的流程图。

qq_22270363的博客 545

音视频入门:H264、H265概念总结

添加链接描述 什么是H.264与H.265 H.264与H.265都是视频压缩格式,由于视频本身的码流太大,所以需要经过压缩然后再通过网络进行传输。H.264是目前比较主流的压缩算法,像视频会议设备一般都采用这个编码格式。基础的H.264可以支持在1M带宽下传输720P30帧/秒的图像;H.264 HIGH PROFILE支持在512K带宽下传输720P30帧/秒的图像。 H.265是比较新的压缩算法,可以更一步提高压缩比,随着我们现在生活中出现的视频格式越来越大(比如现在基本都是1080P甚至4K的显示器

weixin_40673765的博客 8936

Spring 源码分析 spring-core

先来看下 spring-core 的包结构 总共有6个模块,分别是 asm、cglib、core、lang、objenesis、util asm包: 用来操作字节码,动态生成类或者增强既有类的功能。主要包含以下这些类。详细功能。 https://www.ibm.com/developerworks/cn/java/j-lo-asm30/index.html cglib包: ...

likunpeng6656201的博客 542

Oracle数据库安装图文教程 附百度云oracle Database 11g 安装包

一,Oracle数据库安装包下载 1.从Oracle官网下载所需的数据库安装包【下载需要注册登录Oracle账户】     http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html 2.此处有下载好的Oracle Database 11g Release 2  百度云网盘  提取码:z...

向来如此,就一定是对的吗 6万+

【cocos2dx面试题干货】--2021年最新cocos2dx面试干货(引擎篇)

【cocos2dx面试题干货】--2020年度最新cocos2dx面试干货(引擎篇 ) 大家好,我是Lampard~~ 经过春招一番艰苦奋战之后,我终于是进入了心仪的公司。 今天给大家分享一下我在之前精心准备的一套面试知识。 由于我面试的岗位是cocos2dx的开发岗,今天就先给大家分享一下引擎方面的知识。 本人亲测80%的引擎相关题目都...

Lampard的博客 1万+

最详细的Spring核心IOC的源码分析(每次看都有不同的感悟)_spring源码分析

/ 首先,初始化名字为 conversionService 的 Bean。// 因为 内部是可以定义 的,所以这个方法的 root 其实不一定就是 xml 的根节点,也可以是嵌套在里面的 节点,从源码分析的角度,我们当做根节点就好了。// 对采用 @Autowired、@Value 注解的依赖进行设值,这里的内容也是非常丰富的,不过本文不会展开说了,感兴趣的读者请自行研究。// 我们在剖析初始化 Bean 的过程,但是 getBean 方法我们经常是用来从容器中获取 Bean 用的,注意切换思路,

2401_87555332的博客 591

华为OD机试真题 新系统-字符串格式调整(C/C++/Py/Java/Js/Go)

华为OD机试 新系统真题【字符串格式调整】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【字符串格式调整】多语言题解

qq_45776114的博客 1141

Spring 源码分析(一)--整体架构和环境搭建

本系统分析的spring源码版本为4.3.8,参考书《Spring 源码深度解析》,深入学习完后开始整理这本书中知识,分析中代码使用4.3.8版本,有一小部分源码和书中低版本的spring源码有所不同。 (一)整体架构 这些模块被分为以下几个部分 (1)Core Container Core容器(核心容器)包含Core,Beans,Context和Expression...

a8865423的博客 180

手把手教你YOLOv8/YOLOv11分割训练自己数据集和推理,并教你使用Labelme工具标注数据(附分割网络结构图)

YOLOv8-seg 和 YOLOv11-seg 都是一种高效实例分割算法,实例分割模型的输出是一组勾勒出图像中每个物体的遮罩或轮廓,以及每个物体的类标签和置信度分数。当你不仅需要知道物体在图像中的位置,还需要知道它们的具体形状时,实例分割就非常有用了,功能非常强大,可以应对各种视觉任务。 YOLOv11-seg 则在 YOLOv8-seg 基础上进一步魔改了模型架构,相对分割速度可能更快,其他指标性能有待测试。实例分割可以用于自动驾驶、医疗图像处理、智能农业监控等领域。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 4474

利用蓝牙低功耗技术与位置数据,提升邮政运输工具及笼车追踪的效率

BLE 即蓝牙低功耗技术,与无线音箱或各类电脑配件所用的技术类似。正如其名,BLE 的特点是:在实现相近传输范围的同时,功耗显著更低。这让它能应用于对电池续航要求极高的场景。

专业开发者的博客,一个从事Android WiFi和蓝牙的工程师 778

spring 源码分析

这边通过缓存中的 ObjectFactory 拿到的 bean 实例虽然拿到的是 “不完整” 的bean 实例,但是由于是单例,所以后续初始化完成后,该 bean 实例的引用地址并不会变,所以最终我们看到的还是完整 bean 实例。(2)二级缓存earlySingletonObjects:判断bean是否存在AOP,不存在AOP保存半成品的bean(属性暂未填充),存在AOP保存代理bean的beanProxy(目标bean还是半成品的)refresh() 方法是 Spring 上下文启动的核心方法。

qq_51470789的博客 2187

学Simulink--基础微电网场景实例:基于Simulink的微电网并网/孤岛无缝切换控制仿真

本文完成了基于 Simulink 的微电网并网/孤岛无缝切换控制仿真,实现了:✅ 构建含双模式逆变器的微电网模型✅ 实现预同步 + Stateflow 逻辑的智能切换✅ 验证计划与非计划切换的性能边界✅ 证明“无缝”在工程上是可实现的。

amy_mhd的博客 101

史上最详细Spring IOC源码解析(Spring Framework 5.3.x)

Spring IOC容器的本质是一个管理POJO对象生命周期的工厂系统,其核心实现位于和包。本节将深入分析和两大核心接口的源码差异。1.1 BeanFactory源码层级// 核心方法定义(部分)// 层级关系:通过HierarchicalBeanFactory实现父子容器关键实现类的类继承关系:1.2 ApplicationContext的增强通过组合模式集成多个功能接口:// 扩展方法如getEnvironment(), publishEvent()等典型实现类:基础容器。

sadsasdsdsdasda的博客 996

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

原生js读取properties_带你学习inversify.js系列 - inversify基础知识学习

阅读本文前,请熟练掌握以下最最基本概念: Reflect 修饰器 * Ioc对应的几篇小文章可以参考: JS的反射学习和应用 AOP和IoC在点我达前端的实践1、基础概念学习inversify之前,我们需要了解下Ioc的一些概念,建议阅读一下之前写过的一篇文章:AOP和IoC在点我达前端的实践 有了IoC的一个认识之后,我们还需要解释一个概念,这个可以帮助你在后面的应用以及源码阅读的时候不至...

weixin_39676633的博客 633

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Spring容器Container概述

文章目录1.2.1 如何配置`Spring`容器的元数据1.2.2 如何实例化一个`Spring`实例对象1.2.3 `Spring`容器如何使用参考文献 org.springframework.context.ApplicationContext接口表示Spring IoC容器,并负责实例化,配置和组装Bean。容器通过读取配置元数据获取有关要实例化,配置和组装哪些对象的指令。配置元数据以XML,Java注解或Java`代码表示。它使您能够表达组成应用程序的对象以及这些对象之间的丰富相互依赖关系。 ​ S

terry 1290

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Spring容器的初始化过程

https://blog.youkuaiyun.com/qq_39632561/article/details/83070140 1、Spring 容器高层视图 Spring 启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配号Bean之间的依赖关系,为上层应用提供准备就绪的运行环境。 2、内部工作机制 该图描述了Spring容器从加载配置文件到创建出一个完整Bean的作业流程: 1、ResourceLoader从存

liyanlei的专栏 1065

springIOC初始化过程(13个核心方法说明)

springIOC初始化过程(13个核心方法说明)

shuoshuoyou的博客 1322

spring核心容器 spring container 详解

wx5701619的博客 1364
上一篇: 爬虫 Scrapy 学习系列之一:Tutorial
下一篇: Spring Core Container 源码分析七:注册 Bean Definitions
伤神v
博客等级 码龄19年 17粉丝 129原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值