揭秘Spring(二)_Spring之IOC

引:前一篇文章我们介绍了Spring的整体架构以及核心组件,接下来我们将讲解Spring的核心概念之一IOC,我们将先简单介绍IOC与DI,然后再深入SpringIOC容器的工作流程。

IOC与DI

IoC和DI是Spring的两个核心概念,很多人都把它们视为相同的东西(之前自己也一直这样认为),但事实并非如此

  • IoC(Inversion of Control):控制反转。
  • DI(Dependency Injection):依赖注入

开始画重点了: 控制反转是目的,依赖注入是实现控制反转的手段

控制反转是一种设计模式思想,它是一种宽泛的概念,只要一个类将对它内部状态的控制权交由其他机制去完成即为控制反转。控制反转是为了降低类与类之间的耦合度。而Spring采用依赖注入这一具体的手段来达到控制反转的目的。

关于依赖注入可以看我之前的文章先吹响口号_6大设计原则中的依赖倒置原则 DIP一节,这里就不做过多的介绍了。

IOC容器工作流程

IOC容器实际上就是 Context 组件结合其他Bean和Core组件共同构建了一个 Bean 关系网,如何构建这个关系网?构建的入口就在 AbstractApplicationContext 类的 refresh 方法中。这个方法的代码如下:

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
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
// 为刷新准备新的context
this.prepareRefresh();
// **创建BeanFactory**
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// 刷新所有BeanFactory子容器
this.prepareBeanFactory(beanFactory);

try {
// 注册实现了BeanPostProcessor接口的bean(AOP使用)
this.postProcessBeanFactory(beanFactory);
// 初始化和执行BeanFactoryPostProcessor beans
this.invokeBeanFactoryPostProcessors(beanFactory);
// 初始化和执行BeanPostProcessors beans
this.registerBeanPostProcessors(beanFactory);
// 初始化MessageSource
this.initMessageSource();
// 初始化 event multicaster (多路广播)
this.initApplicationEventMulticaster();
// 刷新由子类实现的方法
this.onRefresh();
// 注册事件
this.registerListeners();
// 初始化单例Bean
this.finishBeanFactoryInitialization(beanFactory);
// 发布相应的事件
this.finishRefresh();
} catch (BeansException var9) {
if(this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
// 销毁beans
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}

}
}

这个方法就是构建整个IOC容器过程的完整的代码,了解了里面的每一行代码基本上就了解大部分 Spring 的原理和功能了。

这段代码主要包含这样几个步骤:

  • 构建 BeanFactory
  • 注册可能感兴趣的事件
  • 创建 Bean 实例对象
  • 触发被监听的事件

其中我们我们最关心的就是BeanFactory和创建Bean实例对象了,我们下面可以好好看看:

创建BeanFactory工厂

我们利用debug可以进入到refresh()方法里面的obtainFreshBeanFactory()方法里面的refreshBeanFactory() (有点绕,但是自己就不画时序图了) 去看看他的创建代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected final void refreshBeanFactory() throws BeansException {
if(this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}

try {
// 创建一个DefaultListableBeanFactory(上一节说过的很重要的类)
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
// **这个方法会加载、解析Bean的定义**
this.loadBeanDefinitions(beanFactory);
Object var2 = this.beanFactoryMonitor;
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
}
}

创建Bean实例并构建Bean的关系网

我们利用debug可以进入到refresh()方法里面的finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)方法里面的preInstantiateSingletons() (有点绕,但是自己就不画时序图了) 去看看它的创建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
public void preInstantiateSingletons() throws BeansException {
if(this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}

List<String> beanNames = new ArrayList(this.beanDefinitionNames);
Iterator var2 = beanNames.iterator();

while(true) {
while(true) {
String beanName;
RootBeanDefinition bd;
do {
do {
do {
if(!var2.hasNext()) {
var2 = beanNames.iterator();

while(var2.hasNext()) {
beanName = (String)var2.next();
Object singletonInstance = this.getSingleton(beanName);
if(singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton)singletonInstance;
if(System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
smartSingleton.afterSingletonsInstantiated();
return null;
}
}, this.getAccessControlContext());
} else {
smartSingleton.afterSingletonsInstantiated();
}
}
}

return;
}

beanName = (String)var2.next();
bd = this.getMergedLocalBeanDefinition(beanName);
} while(bd.isAbstract());
} while(!bd.isSingleton());
} while(bd.isLazyInit());

if(this.isFactoryBean(beanName)) {
// 这个Bean很重要,Spring有一大半扩展功能都与这个Bean有关系,
// 它是个工厂Bean,可以产生Bean实例的Bean,
// Spring获取FactoryBean本身的对象是通过在前面加上&来完成的
final FactoryBean<?> factory = (FactoryBean)this.getBean("&" + beanName);
boolean isEagerInit;
if(System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = ((Boolean)AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
return Boolean.valueOf(((SmartFactoryBean)factory).isEagerInit());
}
}, this.getAccessControlContext())).booleanValue();
} else {
isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)factory).isEagerInit();
}

if(isEagerInit) {
// 普通的Bean只要通过getBean方法直接创建它的实例
// getBean方法里面包含了他们关系的创建
this.getBean(beanName);
}
} else {
this.getBean(beanName);
}
}
}
}

IOC容器的扩展点

如何让这些 Bean 对象有一定的扩展性(就是可以加入用户的一些操作)也是我们需要思考的!那么有哪些扩展点呢? Spring 又是如何调用到这些扩展点的?

对 Spring 的 IOC 容器来说,主要有这么几个扩展点:

  • BeanFactoryPostProcessor, BeanPostProcessor:他们分别是在构建 BeanFactory 和构建 Bean 对象时调用。
  • InitializingBean 和 DisposableBean :他们分别是在 Bean 实例创建和销毁时被调用。用户可以实现这些接口中定义的方法,Spring 就会在适当的时候调用他们。
  • FactoryBean :他是个特殊的 Bean,这个 Bean 可以被用户更多的控制。

这些扩展点通常也是我们使用 Spring 来完成我们特定任务的地方,是否精通 Spring 就看你有没有掌握好Spring有哪些扩展点,并且如何使用他们。

要知道如何使用他们就必须了解他们内在的机理。可以用下面一个比喻(优秀)来解释:

我们把 IOC 容器比作一个箱子,这个箱子里有若干个球的模子,可以用这些模子来造很多种不同的球,还有一个造这些球模的机器,这个机器可以产生球模。那么他们的对应关系就是 BeanFactory 就是那个造球模的机器,球模就是 Bean,而球模造出来的球就是 Bean 的实例。

那前面所说的几个扩展点又在什么地方呢? BeanFactoryPostProcessor 对应到当造球模被造出来时,你将有机会可以对其做出设当的修正,也就是他可以帮你修改球模。而 InitializingBean 和 DisposableBean 是在球模造球的开始和结束阶段,你可以完成一些预备和扫尾工作。BeanPostProcessor 就可以让你对球模造出来的球做出适当的修正。最后还有一个 FactoryBean,它可是一个神奇的球模。这个球模不是预先就定型了,而是由你来给他确定它的形状,既然你可以确定这个球模型的形状,当然他造出来的球肯定就是你想要的球了,这样在这个箱子里你可以发现所有你想要的球。

IOC容器的使用

我们使用 Spring 必须要首先构建 IOC 容器,没有它 Spring 无法工作,ApplicatonContext.xml 就是 IOC 容器的默认配置文件,Spring 的所有特性功能都是基于这个 IOC 容器工作的,比如后面要介绍的 AOP。

参考

  1. 《深入分析Java Web技术内幕》
  2. 深入剖析Spring(一)——IoC的基本概念(从面向对象角度介绍)