Spring IoC原理剖析

从顶层视图看:

image-20191013171437365.png-itzhai

本质上的工作原理就是:把应用程序的类和配置元数据组装起来,以便在ApplicationContext创建并初始化好之后,IoC容器直接为你提供了一个已经配置好并且可执行的系统或应用。

1、IoC容器系列的设计与实现:BeanFactory和ApplicationContext

1.1、IoC容器接口设计图

image-20191130120910305

其中的BeanFactory定义了基本的IoC容器的规范

image-20191029233153690

1.2、BeanFactory容器设计原理

以XmlBeanFactory的实现为例来说明简单IoC容器的设计原理:

image-20191029232845474

  • BeanFactory实现是IoC容器的基本形式,各种ApplicationContext的实现是IoC容器的高级表现形式。
  • DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用;
  • Resource是Spring用来封装I/O操作的类;

1.2.1、编程式使用IoC容器

1
2
3
4
5
6
7
ClassPathResource res = new ClassPathResource("spring.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);

SportService sportService = factory.getBean("runSportService1", SportService.class);
sportService.doExercise();

1.3、ApplicationContext

1.3.1、应用场景

image-20191031222218376

ApplicationContext是一个高级形态意义的IoC容器:

  • 支持不同的信息源:继承接口Messagesource;
  • 访问资源:继承接口ResourceLoader;
  • 支持应用事件
  • 在ApplicationContext中提供附加功能;

1.3.2、设计原理

以FileSystemXmlApplicationContext为例子来说明ApplicationContext的设计原理。

image-20191029232656253

AbstractRefreshableApplicationContext

ApplicationContext的主要功能在基类AbstractRefreshableApplicationContext中实现了,来看看FileSystemXmlApplicationContext的构造方法:

1
2
3
4
5
6
7
8
9
10
public FileSystemXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {

super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
ConfigurableApplicationContext

其中的refresh()方法定义在ConfigurableApplicationContext接口中。

ConfigurableApplicationContext接口主要封装配置和生命周期相关方法,这些方法几乎只用在容器启动和关闭的代码中。

ApplicationContext

根据上面的UML图可以知道,ConfigurableApplicationContext的父接口ApplicationContext

  • 继承ListableBeanFactory用于提供访问应用组件的方法;
  • 继承ResourceLoader用于提供加载资源文件的方法;
  • 继承ApplicationEventPublisher用于提供发布事件与注册监听器的方法;
  • 继承MessageSource提供国际化的支持;
AbstractApplicationContext

其中refresh()方法的具体实现在AbstractApplicationContext抽象类中,这个refresh()方法会涉及到IoC容器启动的一系列负载操作,针对不同的容器实现,这些操作都是类似的。

不同的ApplicationContext对应不同的BeanDefinition读取方式,而FileSystemXmlApplicationContext类中提供类一个这样的方法实现:

1
2
3
4
5
6
7
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}

调用这个方法,可以获取到FileSystemResource。

2、IoC容器的初始化

其中IoC容器的初始化过程,主要就体现在AbstractApplicationContextrefresh()方法中,下面是该方法关键代码:

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
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 为刷新流程准备好context
prepareRefresh();

// 2. 加载bean definitions配置并解析注册到BeanFactory中
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// 3. 准备context中的BeanFactory
prepareBeanFactory(beanFactory);

try {
// 4. 设置BeanFactory的后置处理器
postProcessBeanFactory(beanFactory);

// 5. 调用BeanFactory的后置处理器,这些处理器也是context中的beans
invokeBeanFactoryPostProcessors(beanFactory);

// 6. 注册Bean的后置处理器,这些处理器在 bean创建的过程中被调用
registerBeanPostProcessors(beanFactory);

// 7. 对上下文中的消息源进行初始化
initMessageSource();

// 8. 初始化context中的事件机制
initApplicationEventMulticaster();

// 9. 初始化其他特殊的Context中的特殊的bean
onRefresh();

// 10. 检查监听beans,并且向容器注册它们
registerListeners();

// 11. 实例化剩下的所有的(non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// 12. 最后一步:发布容器事件,结束Refresh过程
finishRefresh();
}

catch (BeansException ex) {
// 13. 为防止Bean资源占用,销毁已经在前面过程中已经生成的单间Bean
destroyBeans();
// 14. 重置'active'标志
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}

各个方法功能说明如下:

image-20191117200601820

2.1、为刷新流程准备好context

设置启动日期,记录容器标识位,初始化容器变量。

2.2、加载bean definitions配置并解析注册到BeanFactory中

这里调用具体的子类refreshBeanFactory()方法实现来刷新内部的BeanFactory。

这一步是Bean定义信息载入的环节。最开始会创建一个DefaultListableBeanFactory,保存在ApplicationContext的beanFactory中。这一步的主要的方法是:loadBeanDefinitions(),下面来看看AbstractXMLApplicationContext中该方法的调用层次:

image-20191102103605440

loadBeanDefinitions方法中创建了一个XmlBeanDefinitionReader来读取xml的配置并解析为bean definitions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}

其中loadBeanDefinitions最终会调用到DefaultListableBeanFactory.registerBeanDefinition()方法注册bean definitions。所谓的注册,就是把解析到的BeanDefinition放入到DefaultListableBeanFactory中定义的一个beanDefinitionMap中。调用关系如下:

image-20191102110941571

容器的作用就是对beanDefinitionMap里面的信息进行处理和维护。有了这些数据,就可以进行依赖注入了

2.3、准备context中的BeanFactory

在使用ApplicationContext时需要做一些准备工作,这些准备工作是在这一步处理的,包括:为容器配置ClassLoader、PropertyEditor和BeanPost-Processor等,从而为容器的启动做好必要的准备。

2.4、允许context的子类对BeanFactory进行后处理

postProcessBeanFactory后处理beanFactory。该方法是在所有的beanDenifition加载完成之后,bean实例化之前执行。为了能够修改bean definitions,或者对BeanFactory做一些其他配置,可以使用这个方法。如可以实现ClassPathXmlApplicationContext类并重新该方法即可。Spring中很多ApplicationContext的子类都是通过重写这个方法来达到这个目的(AbstractApplicationContext中的实现为空)。

spring4.2.9 java项目环境下ioc源码分析 (九)——refresh之postProcessBeanFactory方法

2.5、调用BeanFactory的后置处理器,这些处理器也是context中的beans

拿到当前应用上下文 beanFactoryPostProcessors 变量中的值,实例化并调用所有已注册的 BeanFactoryPostProcessor。必须在singleton之前调用。

2.6、注册Bean的后置处理器,这些处理器在 bean创建的过程中被调用

实例化并且注册所有的BeanPostProcessor beans,必须在任何bean实例化之前调用。

2.7、对上下文中的消息源进行初始化

初始化消息源,支持消息的参数化和国际化。

2.8、初始化context中的事件机制

初始化应用事件广播器,这里使用了事件驱动机制。如果自定义了广播器,就用自对应的,否则用默认的。然后把该广播器设置到context的applicationEventMulticaster属性中。

2.9、初始化其他特殊的Context中的特殊的bean

特殊的context子类中初始化其他特殊的bean,使得子类在实例化单例之前,调用初始化bean,默认是空实现。

2.10、检查监听器,并且向容器注册它们

注册监听器,在发布事件的时候会从这里注册的监听器中获取。

2.11、实例化剩下的所有的非懒加载的单例

这里的代码比较多,我们来看看关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
...
getBean(beanName);
...

// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
...
smartSingleton.afterSingletonsInstantiated();
...
}

这里会调用getBean(beanName)触发bean的实例化。

如果bean实现了SmartInitializingSingleton接口,则会执行该接口的afterSingletonsInstantiated方法,该方法是在所有的 单例bean创建完成之后(依赖注入完成,BeanPostProcessor,InitializingBean, initMethod相关方法执行完成后)触发执行,对于懒加载的单例bean无效。

这个接口的功能类似于ContextRefreshedEvent,都是在容器加载完成之后处理其他的事情,但是无需实现ApplicationListener,使用更佳简单。

2.12、最后一步:发布容器事件,结束Refresh过程

完成容器刷新,调用LifecycleProcessor的onRefresh方法,发布ContextRefreshedEvent事件。

关于LifecycleProcessor

负责管理ApplicationContext的生命周期。

3、IoC容器的依赖注入

在没有配置lazy-init=false的情况下,依赖注入的过程是用户第一次向IoC容器索要Bean的时候出发的。具体的触发方法:

BeanFactory.getBean(String name)

来看看这个方法的调用关系:

image-20191102113203553

可以发现,最终是由createBean方法执行。createBean方法生成需要的bean,并且对Bean初始化进行了处理(init-method或者Bean后置处理器等)。

其中createBean方法的RootBeanDefinition对象是在AbstractBeanFactory的doGetBean方法里面生成的:

1
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

如果需要获取的bean是一个子bean定义,那么会遍历父bean的定义,返回一个合并RootBeanDefinition。这个方法会递归一直往上寻找父bean,构造包含继承关系的RootBeanDefinition:

1
2
3
4
5
6
> pbd = getMergedBeanDefinition(parentBeanName);
> ...
> mbd = new RootBeanDefinition(pbd);
> // 使用bd(可能是子bean)覆盖mbd(可能是父bean的副本),这样就把父bean和子bean的属性配置合并在一起了。
> mbd.overrideFrom(bd);
>

方法中的RootBeanDefinition,如2.7、Bean定义继承,指的是父定义。在配置文件中可以定义父<bean>和子<bean>,父<bean>用RootBeanDefinition表示,而子<bean>用ChildBeanDefiniton表示,而没有父<bean><bean>就使用RootBeanDefinition表示。see Spring中BeanDefinition的继承体系

3.1、在createBean这个方法包含以下方法调用:

Bean实例化

调用关系如下

image-20191102120151297

以下是instantiate方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (!bd.hasMethodOverrides()) {
Constructor<?> constructorToUse;
...
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);
}
}

可以发现,如果是接口的实现,那么就会使用CGLIB来实例化Bean,否则使用BeanUtils中的JVM反射来实例化Bean。生成的实例最终会被封装为BeanWrapper,BeanWrapper相当于一个代理器,Spring委托BeanWrapperwancehngBean属性的填充工作。在Bean实例被InstantiatioonStrategy创建出来之后,容器主控程序将Bean实例通过BeanWrapper包装起来,这是通过调用BeanWrapper#setWrappedInstance(Object obj)方法完成的。see Spring容器技术内幕之BeanWrapper类介绍

Bean的初始化,依赖注入一般发生在这里

createBean中调用了populateBean方法,该方法进行了依赖注入处理,主要通过bean definition中的属性值填充BeanWrapper中的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
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
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// null实例不能处理属性依赖注入
if (bw == null) {
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}

// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
boolean continueWithPropertyPopulation = true;

if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}

if (!continueWithPropertyPopulation) {
return;
}
// 获取在BeanDefinition中设置的property值
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}

boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}

if (pvs != null) {
// 对属性进行注入
applyPropertyValues(beanName, mbd, bw, pvs);
}
}

其中最后一个方法调用applyPropertyValues是属性注入的处理方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
...
List<PropertyValue> original;
...
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
...
for (PropertyValue pv : original) {
...
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
...
}
...
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
...
}

这里通过BeanDefinitionValueResolver来对BeanDefinition进行解析,然后注入到PropertyValues中。

主要的处理逻辑是为解析值创建一个副本:

1
List<PropertyValue> deepCopy = new ArrayList<>(original.size());

副本的数据最后会被注入到Bean中:

1
bw.setPropertyValues(new MutablePropertyValues(deepCopy));

其中resolveValueIfNecessary方法主要的作用是:

解析PropertyValue中对工厂中其他bean的任何引用。该值可以是:

  • 一个BeanDefinition,它导致创建相应的新bean实例;
  • 一个RuntimeBeanReference;
  • 一个ManagedList。 这是一个特殊的集合,其中可能包含需要解析的RuntimeBeanReferences或Collections;
  • 一个ManagedSet。 也可能包含需要解决的RuntimeBeanReferences或Collections;
  • 一个ManagedMap。 在这种情况下,该值可能是需要解析的RuntimeBeanReference或Collection;
  • 一个普通对象或null,在这种情况下,将不做解析。

而最后的setPropertyValues方法会为BeanWraper完成Bean的属性值注入。

总的来说,以getBean为入口,触发Bean的创建和依赖注入,其间需要依赖BeanDefinition中的信息来递归地完成依赖注入。

References

Spring中BeanDefinition的继承体系

Spring容器技术内幕之BeanWrapper类介绍

Spring SmartInitializingSingleton

arthinking wechat
欢迎关注itzhai公众号
  • 本文作者: arthinking
  • 本文链接: spring-ioc-theory-analyse.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!