Spring IoC 源码解析:容器的基本层次结构

控制反转(IoC: Inversion of Control)是 Spring Framework 的核心基础特性,也是面向对象程序设计中的重要原则,其目的在于降低程序之间的耦合度。控制反转一般分为 依赖注入(DI: Dependency Injection)依赖查找(DL: Dependency Lookup) 两种类型,不过依赖注入应用更加广泛,所以大部分时候依赖注入等同于控制反转。

在面向对象程序设计中,对象一般用于承载和处理数据,不同对象之间的相互依赖与合作构成了我们的软件系统。设想在大型软件系统设计中,需要大量的对象通过相互依赖和交互进行合作,如果这些依赖关系由对象自己去控制和管理,那么耦合度将会非常高,不易于系统的扩展和维护。这个时候我们可以将对象的依赖关系交由 IoC 容器进行管理,将对象的新建和引用赋值等操作交由 IoC 容器统一完成,而对象只需要专心负责承载和处理数据即可。这样的设计可以降低系统在实现上的复杂性和耦合度,让系统更加灵活,满足“开闭原则”,并易于扩展和维护。

Spring IoC 是 IoC 设计原则的轻量化实现,纵览 Spring IoC 容器类的 UML 图(如下)将会发现 Spring IoC 容器的设计可以分为两条主线:

  1. 实现了 BeanFactory 接口的简单容器。
  2. 以 ApplicationContext 应用上下文为核心的高级容器。

其中 ApplicationContext 相对于 BeanFactory 而言增加了许多高级特性,让原本在 BeanFactory 中需要编码实现的功能,简化到用配置或注解即可完成。

image

如上图所示,若以一条直线从中间将该图分为左右两半的话,那么简单容器结构主要分布在左半部分,继承路径为:

1
2
3
4
5
BeanFactory
--> HierarchicalBeanFactory
-----> ConfigurableBeanFactory
--------> ConfigurableListableBeanFactory
-----------> DefaultListableBeanFactory

而高级容器结构主要分布在右半部分,当然高级容器的实现是建立在简单容器基础之上的,继承路径为:

1
2
3
4
BeanFactory
--> ListableBeanFactory
-----> ApplicationContext
--------> ConfigurableApplicationContext & WebApplicationContext

Bean 的内存表示

现实中的容器用来盛放物品,Spring 的容器也不例外,这里的物品就是 bean 定义和实例。我们通常对于 bean 的印象是一个个躺在配置文件中的 <bean/> 标签,或者是被注解的类,但是这些都是 bean 的静态形式,是还没有被放入容器的物料,最终(加载完配置之后,调用 BeanFactory#getBean 之前)加载到容器中的是一个个 BeanDefinition 对象。

BeanDefinition 的继承关系如下图所示,其中 RootBeanDefinition、ChildBeanDefinition,以及 GenericBeanDefinition 是三个主要的类实现。有时我们需要在配置时通过 parent 属性指定 bean 之间的父子关系,这时父 bean 采用 RootBeanDefinition 表示,而子 bean 则采用 ChildBeanDefinition 表示。GenericBeanDefinition 自 2.5 版本引入,是服务于一般的 bean 定义的一站式中心。

image

这三个类都是由 AbstractBeanDefinition 抽象类派生而来,该抽象类中包含了 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
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable {

// ... 省略常量定义

/** 对应的类 Class 对象 */
private volatile Object beanClass;
/** 作用域,对应 scope 属性 */
private String scope = SCOPE_DEFAULT;
/** 抽象类标识,对应 abstract 属性 */
private boolean abstractFlag = false;
/** 延迟加载标识,对应 lazy-init 属性 */
private Boolean lazyInit;
/** 自定装载类型,对应 autowire 配置 */
private int autowireMode = AUTOWIRE_NO;
/** 依赖检查,对应 dependency-check 属性 */
private int dependencyCheck = DEPENDENCY_CHECK_NONE;
/** 对应 depends-on 属性,表示一个 bean 实例化前置依赖另一个 bean */
private String[] dependsOn;
/** 对应 autowire-candidate 属性,设置为 false 时表示取消当前 bean 作为自动装配候选者的资格 */
private boolean autowireCandidate = true;
/** 对应 primary 属性,当自动装配存在多个候选者时,将当前 bean 作为首选 */
private boolean primary = false;
/** 对应 qualifier 属性 */
private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
/** 创建 bean 实例时的回调函数 */
private Supplier<?> instanceSupplier;
/** 非配置项,表示允许访问非公开的构造器和方法,由程序设置 */
private boolean nonPublicAccessAllowed = true;
/**
* 非配置项,表示是否允许以宽松的模式解析构造函数,由程序设置
*
* 例如:如果设置为 true,则在下列情况时不会抛出异常(示例来源于《Spring 源码深度解析》)
* interface ITest{}
* class ITestImpl implements ITest {}
* class Main {
* Main(ITest i){}
* Main(ITestImpl i){}
* }
*/
private boolean lenientConstructorResolution = true;
/** 对应 factory-bean 属性 */
private String factoryBeanName;
/** 对应 factory-method 属性 */
private String factoryMethodName;
/** 构造函数注入属性,对应 <construct-arg/> 标签 */
private ConstructorArgumentValues constructorArgumentValues;
/** 记录 <property/> 属性集合 */
private MutablePropertyValues propertyValues;
/** 记录 <lookup-method/> 和 <replaced-method/> 标签配置 */
private MethodOverrides methodOverrides = new MethodOverrides();
/** 对应 init-method 属性 */
private String initMethodName;
/** 对应 destroy-method 属性 */
private String destroyMethodName;
/** 非配置项,是否执行 init-method,由程序设置 */
private boolean enforceInitMethod = true;
/** 非配置项,是否执行 destroy-method,由程序设置 */
private boolean enforceDestroyMethod = true;
/** 非配置项,表示 bean 是否是用户定义而不是程序定义的,创建 AOP 时为 true,由程序设置 */
private boolean synthetic = false;
/**
* 非配置项,定义 bean 的应用场景,由程序设置,角色如下:
* ROLE_APPLICATION:用户
* ROLE_INFRASTRUCTURE:完全内部使用
* ROLE_SUPPORT:某些复杂配置的一部分
*/
private int role = BeanDefinition.ROLE_APPLICATION;
/** 描述信息,对应 description 标签 */
private String description;
/** 定义的资源 */
private Resource resource;

// ... 省略方法定义
}

BeanDefinition 是 Spring IoC 容器表示 bean 配置的内部数据结构,Spring 将各个 bean 对应的 BeanDefinition 实例注册记录在 BeanDefinitionRegistry 中,该接口定义了对 BeanDefinition 的各种增删查操作,类似于内存数据库,其实现类 SimpleBeanDefinitionRegistry 主要以 Map 作为存储介质。

简单 IoC 容器

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
36
37
38
39
40
41
42
43
public interface BeanFactory {
/**
* 用户使用容器时可以使用转义符“&”来得到 FactoryBean 实例,用来区分通过容器获取的是 FactoryBean 产生的对象还是获取 FactoryBean 实例本身,
* 例如:如果 myBean 是一个 FactoryBean,那么使用“&myBean”得到的是 FactoryBean 实例,而不是 myBean 这个由 FactoryBean 构造的实例
*/
String FACTORY_BEAN_PREFIX = "&";

/** 根据 bean 的名字获取对应的 bean 实例 */
Object getBean(String name) throws BeansException;

/** 根据 bean 的名字获取对应的 bean 实例,增加了对象类型检查 */
<T> T getBean(String name, Class<T> requiredType) throws BeansException;

/** 根据 bean 的名字获取对应的 bean 实例,可以指定构造函数的参数或者工厂方法的参数 */
Object getBean(String name, Object... args) throws BeansException;

/** 根据 bean 类型获取对应的 bean 实例 */
<T> T getBean(Class<T> requiredType) throws BeansException;

/** 根据 bean 类型获取对应的 bean 实例,可以指定构造函数的参数或者工厂方法的参数 */
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

/** 判断容器是否持有指定名称的 bean 实例 */
boolean containsBean(String name);

/** 是不是单例 */
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

/** 是不是原型对象 */
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

/** 判断 name 对应的 bean 实例是不是指定 Class 类型 */
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

/** 判断 name 对应的 bean 实例是不是指定 Class 类型 */
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

/** 获取 bean 实例的 Class 对象 */
Class<?> getType(String name) throws NoSuchBeanDefinitionException;

/** 获取 bean 的所有别名,如果不存在则返回空 */
String[] getAliases(String name);
}

BeanFactory 中定义的各个方法及其作用如上述代码注释。整个设计还是比较简洁和直观的,其中将近一半是获取 bean 对象的方法重载,另外就是对 bean 属性的获取和判定。BeanFactory 接口仅仅是定义了 IoC 容器的最基本形式,具体实现都交由子类完成,后面我们会举例说明。

FactoryBean 与 BeanFactory

Spring 在 BeanFactory 接口中定义了一个 FACTORY_BEAN_PREFIX 常量,用来获取 FactoryBean 对象,这个需要与我们本节所讨论的 BeanFactory 相区分开来,虽然两者在名字上很相似,但却是完全不同的两个类。BeanFactory 以 Factory 结尾,顾名思义它是一个工厂,用来管理 bean 对象,而 FactoryBean 则以 Bean 结尾,说明它本质上还是一个 bean,只是比我们通常所见的 bean 稍微特殊了一点。

FactoryBean 接口的定义如下,主要是用来构造一些复杂对象。如果一个对象的配置十分复杂,通过编码实现相对于配置可能是更好的选择。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface FactoryBean<T> {

/** 获取由 FactoryBean 创建的 bean 实例*/
T getObject() throws Exception;

/** 返回由 FactoryBean 创建的 bean 的 Class 类型 */
Class<?> getObjectType();

/** 是否是单实例 */
default boolean isSingleton() {
return true;
}

}

下面举例说明 FactoryBean 的用法。假设有一个接口 Printer 和两个实现类:OddPrinter 和 EvenPrinter,定义如下:

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
public interface Printer {

void print(int x);

}

@Component
public class EvenPrinter implements Printer {

@Override
public void print(int x) {
System.out.println("even: " + x);
}

}

@Component
public class OddPrinter implements Printer {

@Override
public void print(int x) {
System.out.println("odd: " + x);
}

}

OddPrinter 和 EvenPrinter 的功能很简单,分别用于打印奇数和偶数,假设现在有这样一个需求:对于一个入参 x,如果 x 是偶数则调用 EvenPrinter 实例的 EvenPrinter#print 方法,否则调用 OddPrinter 实例的 OddPrinter#print 方法,也就是说我们需要依据入参的值动态选择 Printer 的实例。

针对上述需求,基于 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
@Component("printer-factory")
public class PrinterFactory extends AbstractFactoryBean<Printer> {

private final Printer printer;

private final OddPrinter oddPrinter;
private final EvenPrinter evenPrinter;

public PrinterFactory(@Autowired OddPrinter oddPrinter, @Autowired EvenPrinter evenPrinter) {
this.oddPrinter = oddPrinter;
this.evenPrinter = evenPrinter;
this.printer = new ProxyPrinter();
}

@Override
protected Printer createInstance() throws Exception {
return this.printer;
}

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

private class ProxyPrinter implements Printer {

@Override
public void print(int x) {
if (x % 2 == 0) {
evenPrinter.print(x);
} else {
oddPrinter.print(x);
}
}
}

}

主函数:

1
2
3
4
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-core.xml");
final Printer printer = (Printer) context.getBean("printer-factory");
printer.print(1); // odd: 1
printer.print(2); // even: 2

基于 FactoryBean 可以利用静态 IoC 来模拟动态 IoC。

由上述示例可以看出当调用 BeanFactory#getBean 方法获取名为 printer-factory 的 bean 时,实际返回的却是 Printer 类实例,如果我们希望返回 ProxyPrinter 类示例,可以在 printer-factory 名称前加 & 标识符,即 context.getBean("&printer-factory"),这样就可以拿到 FactoryBean 实例。

基本结构设计

BeanFactory 定义了容器的基本形式,Spring 又在此基础上逐层扩展以丰富容器的特性,如下图所示:

image

下面针对主要的类和接口的功能进行一个简单的介绍:

  • HierarchicalBeanFactory

HierarchicalBeanFactory 译为中文是分层的 BeanFactory,它相对于 BeanFactory 增加了对父 BeanFactory 的获取。下层 IoC 容器(也叫子容器)可以通过 HierarchicalBeanFactory#getParentBeanFactory 方法访问父 IoC 容器,让容器的设计具备了层次性。这种层次性增强了容器的扩展性和灵活性,我们可以通过编程的方式为一个已有的容器添加一个或多个子容器,从而实现一些特殊功能。

层次容器的一个特点就是子容器对于父容器来说是透明的,而子容器则能感知到父容器的存在。典型的应用场景就是 Spring MVC,控制层的 bean 位于子容器中,而业务层和持久层的 bean 则位于父容器中,这样的设计可以让控制层的 bean 访问业务层和持久层的 bean,反之则不行,从而在容器层面对三层软件结构设计提供约束。

  • ListableBeanFactory

ListableBeanFactory 中文译为可列举的 BeanFactory,对于 IoC 容器而言,bean 的定义和属性是可以列举的对象。

ListableBeanFactory 相对于 BeanFactory 增加了获取容器中 bean 的配置信息的若干方法,比如获取容器中 bean 的个数、获取容器中所有 bean 的名称列表、按照目标类型获取 bean 名称,以及检查容器中是否包含指定名称的 bean 等等。

  • AutowireCapableBeanFactory

AutowireCapableBeanFactory 提供了创建 bean 实例、自动注入、初始化,以及应用 bean 的后置处理器等功能。自动注入让配置变得更加简单,也让注解配置成为可能,Spring 目前提供了四种自动注入类型:

  1. byName :根据名称自动注入,假设 bean A 有一个名为 b 的属性,如果容器中刚好存在一个名称为 b 的 bean,则将该 bean 注入给 bean A 的 b 属性。
  2. byType :根据类型自动注入,假设 bean A 有一个类型为 B 的属性,如果容器中刚好存在一个类型为 B 的 bean,则将该 bean 注入给 bean A 对应的属性。
  3. constructor :仅针对构造方法注入而言,类似于 byType,如果 bean A 有一个包含 B 类型入参的构造方法,如果容器中有一个类型为 B 的 bean,则使用该 bean 作为入参,如果找不到则抛出异常。
  4. autodetect :根据 bean 的自省机制决定采用 byType 还是 constructor 进行自动注入,如果 bean 提供了默认的构造函数,则采用 byType,否则采用 constructor。

标签 <beans/> 中的 default-autowire 属性可以配置为全局自动匹配,默认值为 no,表示不启用自动装配。在实际开发中,基于 XML 配置的方式很少启用自动装配功能,而基于注解的配置方式则默认采用 byType 自动装配策略。

  • ConfigurableBeanFactory

ConfigurableBeanFactory 定义了配置 BeanFactory 的各种方法,增强了 IoC 容器的可定制性,包括设置类装载器、属性编辑器,以及容器初始化后置处理器等方法。

  • DefaultListableBeanFactory

DefaultListableBeanFactory 是一个非常重要的类,定义了 IoC 容器所应该具备的重要功能,是容器完整功能的基本实现。XmlBeanFactory 是一个典型的由该类派生出来的 BeanFactory,并且只是增加了加载 XML 配置资源的逻辑,而容器相关的特性则全部由 DefaultListableBeanFactory 来实现。XmlBeanFactory 类的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class XmlBeanFactory extends DefaultListableBeanFactory {

private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
// 加载 XML 资源
this.reader.loadBeanDefinitions(resource);
}
}

我们将在下一篇中分析 XmlBeanFactory 加载 bean 的基本过程。Spring 在 3.1 版本之后将 XmlBeanFactory 标记为 deprecated,并推荐使用更加原生的方式,即组合使用 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 来取代 XmlBeanFactory 的功能。

高级 IoC 容器

ApplicationContext 是 Spring 为开发者提供的高级 IoC 容器形式,也是我们初始化 Spring 容器的常用方式。除了具备简单容器所有的功能外,ApplicationContext 还提供了许多额外功能以降低开发人员的开发量,提升框架的使用效率。这些额外的功能主要包括:

  • 国际化支持 :实现了 MessageSource 接口,为容器提供国际化消息访问功能,支持具备多语言版本需求的应用开发,并提供了多种实现来简化国际化资源文件的装载和获取。
  • 发布应用上下文事件 :实现了 ApplicationEventPublisher 接口,让容器拥有发布应用上下文事件的功能,包括容器启动、关闭事件等。如果一个 bean 需要接收容器事件,则只需要实现 ApplicationListener 接口即可,Spring 会自动扫描对应的监听器配置,并注册成为事件的订阅者。
  • 丰富的资源获取的方式 :实现了 ResourcePatternResolver 接口,该接口的实现类 PathMatchingResourcePatternResolver 让我们可以采用 Ant 风格的资源路径去加载配置文件。

基于 ApplicationContext 派生出了众多的扩展实现,如下图所示:

image

ConfigurableApplicationContext 和 WebApplicationContext 是直接实现 ApplicationContext 的两个接口。

  • ConfigurableApplicationContext

ConfigurableApplicationContext 中主要增加了 ConfigurableApplicationContext#refreshConfigurableApplicationContext#close 两个方法,从而为应用上下文提供了启动、刷新和关闭的能力。其中 ConfigurableApplicationContext#refresh 方法是高级容器的核心方法,该方法概括了高级容器初始化的主要流程(包含简单容器的全部功能,以及高级容器扩展的功能),比如我们通常会采用如下方式启动高级容器:

1
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-core.xml");

在执行 new ClassPathXmlApplicationContext("spring-core.xml") 时,本质上就是在触发 ConfigurableApplicationContext#refresh 方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
// 支持多个配置文件以数组形式传入
this.setConfigLocations(configLocations);
if (refresh) {
// 加载配置,并初始化 IoC 容器
this.refresh();
}
}

在调用 ConfigurableApplicationContext#refresh 方法之前,Spring 会先去解析配置文件的路径并存储到字符串数组中,然后开始执行容器的初始化逻辑。该方法的实现位于 AbstractApplicationContext 抽象类中,如下所示:

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
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 1. 初始化待 refresh 的上下文环境
prepareRefresh();
// 2. 初始化 BeanFactory,加载并解析配置
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

/* 至此,完成了简单容器的所有功能,下面开始对简单容器进行增强 */

// 3. 对 BeanFactory 进行功能增强
prepareBeanFactory(beanFactory);
try {
// 4. 模板方法,后置处理 BeanFactory 实例
postProcessBeanFactory(beanFactory);
// 5. 调用已注册的 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 6. 注册 BeanPostProcessor,这里仅仅是注册,调用发生在 getBean 的时候
registerBeanPostProcessors(beanFactory);
// 7. 初始化国际化资源
initMessageSource();
// 8. 初始化事件广播器
initApplicationEventMulticaster();
// 9. 模板方法
onRefresh();
// 10. 注册事件监听器
registerListeners();
// 11. 实例化所有非延迟加载的单例
finishBeanFactoryInitialization(beanFactory);
// 12. 完成 refresh 过程,发布相关事件
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

后续的篇章将详细分析上述整个过程的源码实现,这里只需要了解整个初始化的整体流程即可。

  • WebApplicationContext

WebApplicationContext 是为 WEB 应用定制的上下文类,基于 servlet 实现配置文件的加载和初始化工作。对于非 WEB 应用而言,bean 只有 singleton 和 prototype 两种作用域,而在 WebApplicationContext 中则新增了 request、session、globalSession,以及 application 四种作用域。

WebApplicationContext 将整个应用上下文对象以属性的形式记录到 ServletContext 中,我们可以通过 WebApplicationContextUtils#getWebApplicationContext 工具方法从 ServletContext 对象中获取 WebApplicationContext 实例。

为了支持这一特性,WebApplicationContext 类定义了一个常量:

1
ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"

在初始化应用上下文时会以该常量为 key,将 WebApplicationContext 实例存放到 ServletContext 的属性列表中。当我们调用 WebApplicationContextUtils#getWebApplicationContext 工具方法时,本质上是在调用 ServletContext#getAttribute 方法,不过 Spring 会对获取的结果做一些校验工作。

总结

总的来说,Spring IoC 容器的基本设计主要分为 BeanFactory 和 ApplicationContext 两大部分,本文中我们对整个容器的结构层次进行了简单的介绍,目的在于从整体上对 Spring IoC 容器建立起感官上认识。

参考

  1. Spring 源码深度解析