架构

Spring Framework

Spring Framework

Spring 核心容器 Core Container

  • Spring Core
  • Spring Bean
  • Spring Context
  • SpEL (Spring Expression Language)

数据访问 Data Access

  • JDBC
  • ORM
  • OXM
  • Transaction

Web

  • WebMVC
  • WebFlux
  • WebSocket

AOP

  • AOP
  • Aspects

其他

  • JMS (Java Messaging Service)

  • Test

  • Messaging

使用 Spring 框架带来的主要好处:

  • DI :**Dependency Injection(DI)** 方法,使得构造器和 JavaBean、properties 文件中的依赖关系一目了然。
  • 轻量级:与 EJB 容器相比较,IoC 容器更加趋向于轻量级。这样一来 IoC 容器在有限的内存和 CPU 资源的情况下,进行应用程序的开发和发布就变得十分有利。
  • 面向切面编程(AOP): Spring 支持面向切面编程,同时把应用的业务逻辑与系统的服务分离开来。
  • 集成主流框架:Spring 并没有闭门造车,Spring 集成了已有的技术栈,比如 ORM 框架、Logging 日期框架、J2EE、Quartz 和 JDK Timer ,以及其他视图技术。
  • 模块化:Spring 框架是按照模块的形式来组织的。由包和类的命名,就可以看出其所属的模块,开发者仅仅需要选用他们需要的模块即可。
  • 便捷的测试:要 测试一项用Spring开发的应用程序 十分简单,因为测试相关的环境代码都已经囊括在框架中了。更加简单的是,利用 JavaBean 形式的 POJO 类,可以很方便的利用依赖注入来写入测试数据。
  • Web 框架:Spring 的 Web 框架亦是一个精心设计的 Web MVC 框架,为开发者们在 Web 框架的选择上提供了一个除了主流框架比如 Struts 、过度设计的、不流行 Web 框架的以外的有力选项。
  • 事务管理:Spring 提供了一个便捷的事务管理接口,适用于小型的本地事物处理(比如在单 DB 的环境下)和复杂的共同事物处理(比如利用 JTA 的复杂 DB 环境)。
  • 异常处理:Spring 提供一个方便的 API ,将特定技术的异常(由JDBC, Hibernate, 或 JDO 抛出)转化为一致的、Unchecked 异常。

设计模式

  • 代理模式 — 在 AOP 和 remoting 中被用的比较多。
  • 单例模式 — 在 Spring 配置文件中定义的 Bean 默认为单例模式。
  • 模板方法 — 用来解决代码重复的问题。比如 RestTemplate、JmsTemplate、JdbcTemplate 。
  • 工厂模式 — BeanFactory 用来创建对象的实例。

前言

  • IOC ,侧重在于容器。
  • Bean ,侧重在于被容器管理的 Bean 。

容器创建 Bean 对象,将它们装配在一起,配置它们并管理它们的完整生命周期。

依赖注入 (DI)

在依赖注入中,你不必主动、手动创建对象,但必须描述如何创建它们。

  • 你不是直接在代码中将组件和服务连接在一起,而是描述配置文件中哪些组件需要哪些服务。
  • 然后,再由 IoC 容器将它们装配在一起。

注入方式

  • 接口注入
  • 构造函数注入
  • setter 注入

Spring 提供了两种( 不是“个” ) IoC 容器,分别是 BeanFactory、ApplicationContext 。

  • BeanFactory

BeanFactory 在 spring-beans 项目提供。

BeanFactory ,就像一个包含 Bean 集合的工厂类。它会在客户端要求时实例化 Bean 对象。

  • ApplicationContext

ApplicationContext 在 spring-context 项目提供。

ApplicationContext 接口扩展了 BeanFactory 接口,它在 BeanFactory 基础上提供了一些额外的功能

Spring Bean 支持 5 种 Scope

  • Singleton - 每个 Spring IoC 容器仅有一个单 Bean 实例。默认
  • Prototype - 每次请求都会产生一个新的实例。
  • Request - 每一次 HTTP 请求都会产生一个新的 Bean 实例,并且该 Bean 仅在当前 HTTP 请求内有效。
  • Session - 每一个的 Session 都会产生一个新的 Bean 实例,同时该 Bean 仅在当前 HTTP Session 内有效。
  • Application - 每一个 Web Application 都会产生一个新的 Bean ,同时该 Bean 仅在当前 Web Application 内有效。

Spring Bean 在容器的生命周期

Spring Bean 的初始化流程如下:

  • 实例化 Bean 对象
  • Aware 相关的属性,注入到 Bean 对象
  • 调用相应的方法,进一步初始化 Bean 对象

流程图

实例化->设置属性->初始化Bean对象->销毁

什么是 Spring 装配

当 Bean 在 Spring 容器中组合在一起时,它被称为装配Bean 装配

装配,和 DI 依赖注入,实际是一个东西。

  • no - 这是默认设置,表示没有自动装配。应使用显式 Bean 引用进行装配。
  • byName - 它根据 Bean 的名称注入对象依赖项。它匹配并装配其属性与 XML 文件中由相同名称定义的 Bean 。
  • 【最常用】byType - 它根据类型注入对象依赖项。如果属性的类型与 XML 文件中的一个 Bean 类型匹配,则匹配并装配属性。

延迟加载 在Bean 中设置 lzay-init = "true"

Spring 的单例 Bean 是线程安全的。

循环依赖的问题见https://hankz.cc/2021/03/07/%E4%BB%80%E4%B9%88%E6%98%AFspring%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/

AOP(Aspect-Oriented Programming),即面向切面编程

  • 在 OOP 中,以类( Class )作为基本单元
  • 在 AOP 中,以**切面( Aspect )**作为基本单元。

实现 AOP 的技术,主要分为两大类:

静态代理 - 指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强。

动态代理 - 在运行时在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强。目前 Spring 中使用了两种动态代理库:

  • JDK 动态代理

    ​ JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是 InvocationHandler 接口和 Proxy 类。

  • CGLIB

    ​ 如果目标类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。当然,Spring 也支持配置,强制使用 CGLIB 动态代理。

Spring Transaction

事务就是对一系列的数据库操作(比如插入多条数据)进行统一的提交或回滚操作,如果插入成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。

事务的特性ACID

  1. 原子性 Atomicity :一个事务中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。
  2. 一致性 Consistency :在事务开始之前和事务结束以后,数据库的完整性没有被破坏。
  3. 隔离性 Isolation :数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
  4. 持久性 Durability :事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失

事务管理

  • 声明式事务:通过使用注解或基于 XML 的配置事务
  • 编程式事务:通过编码的方式实现事务管理,需要在代码中显式的调用事务的获得、提交、回滚。

一般使用 Spring Boot + 注解的声明式事务

事务隔离级别

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
// TransactionDefinition.java

/**
* 【Spring 独有】使用后端数据库默认的隔离级别
*
* MySQL 默认采用的 REPEATABLE_READ隔离级别
* Oracle 默认采用的 READ_COMMITTED隔离级别
*/
int ISOLATION_DEFAULT = -1;

/**
* 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
*/
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;

/**
* 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
*/
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
/**
* 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
*/
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
/**
* 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
*
* 但是这将严重影响程序的性能。通常情况下也不会用到该级别。
*/
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

IOC

理论

IoC 全称为 Inversion of Control,翻译为 “控制反转”(DI)

  1. 谁控制谁:在传统的开发模式下,我们都是采用直接 new 一个对象的方式来创建对象,也就是说你依赖的对象直接由你自己控制,但是有了 IoC 容器后,则直接由 IoC 容器来控制。所以“谁控制谁”,当然是 IoC 容器控制对象
  2. 控制什么:控制对象。
  3. 为何是反转:没有 IoC 的时候我们都是在自己对象中主动去创建被依赖的对象,这是正转。但是有了 IoC 后,所依赖的对象直接由 IoC 容器创建后注入到被注入的对象中,依赖的对象由原来的主动获取变成被动接受,所以是反转。
  4. 哪些方面反转了:所依赖对象的获取被反转了。
  • Resource 体系

  • ResourceLoader 体系

  • BeanFactory 体系

  • BeanDefinition 体系

  • ApplicationContext 体系

  • BeanDefinitionReader 体系

BeanDefinition 解析

1
2
3
4
ClassPathResource resource = new ClassPathResource("bean.xml"); // <1>
DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // <2>
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); // <3>
reader.loadBeanDefinitions(resource); // <4>

这段代码是 Spring 中编程式使用 IoC 容器

  1. 获取资源
  2. 获取 BeanFactory
  3. 根据新建的 BeanFactory 创建一个 BeanDefinitionReader 对象,该 Reader 对象为资源的解析器
  4. 装载资源

资源定位、装载、注册

DTD 与 XSD 的区别

DTD(Document Type Definition),即文档类型定义,为 XML 文件的验证机制,属于 XML 文件中组成的一部分。DTD 是一种保证 XML 文档格式正确的有效验证方式,它定义了相关 XML 文档的元素、属性、排列方式、元素的内容类型以及元素的层次结构。

XSD(XML Schemas Definition)即 XML Schema 语言。XML Schema 本身就是一个 XML文档,使用的是 XML 语法,因此可以很方便的解析 XSD 文档。

1
org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions
  1. 调用 #getValidationModeForResource(Resource resource) 方法,获取指定资源(xml)的验证模式
  2. 调用 DocumentLoader#loadDocument(InputSource inputSource, EntityResolver entityResolver,ErrorHandler errorHandler, int validationMode, boolean namespaceAware) 方法,获取 XML Document 实例。
  3. 调用 #registerBeanDefinitions(Document doc, Resource resource) 方法,根据获取的 Document 实例,注册 Bean 信息。

BeanDefinition 的解析过程

时序图

Bean

Bean解析

Spring 中有两种解析 Bean 的方式:

  • 如果根节点或者子节点采用默认命名空间的话,则调用 #parseDefaultElement(...) 方法,进行默认标签解析
  • 否则,调用 BeanDefinitionParserDelegate#parseCustomElement(...) 方法,进行自定义解析。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//DefaultBeanDefinitionDocumentReader.java

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();

for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element)node;
if (delegate.isDefaultNamespace(ele)) {
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}

}

Bean加载

当我们显示或者隐式地调用 BeanFactory#getBean(String name) 方法时,则会触发加载 Bean 阶段

1
2
3
4
5
//AbstractBeanFactory.java
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}

内部调用 doGetBean(String name, final Class<T> requiredType, Object[] args, boolean typeCheckOnly) 方法,其接受四个方法参数:

  • name :要获取 Bean 的名字
  • requiredType :要获取 bean 的类型
  • args :创建 Bean 时传递的参数。这个参数仅限于创建 Bean 时使用。
  • typeCheckOnly :是否为类型检查。

Spring Bean 加载过程的一个概览

  1. 分析从缓存中获取单例 Bean ,以及对 Bean 的实例中获取对象。
  2. 如果从单例缓存中获取 Bean ,Spring 是怎么加载的呢?所以第二部分是分析 Bean 加载,以及 Bean 的依赖处理。
  3. Bean 已经加载了,依赖也处理完毕了,第三部分则分析各个作用域的 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
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {

// 如果 name 是 alias ,则获取对应映射的 beanName 。
String beanName = transformedBeanName(name);
Object beanInstance;

// 从缓存中或者实例工厂中获取 Bean 对象
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//完成 FactoryBean 的相关处理,并用来获取 FactoryBean 的处理结果
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}

else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
//因为 Spring 只解决单例模式下得循环依赖,在原型模式下如果存在循环依赖则会抛出异常。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}

//如果容器中没有找到,则从父类容器中加载
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}

//如果不是仅仅做类型检查则是创建bean,这里需要记录
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}

StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
.tag("beanName", name);
try {
if (requiredType != null) {
beanCreation.tag("beanType", requiredType::toString);
}
//从容器中获取 beanName 相应的 GenericBeanDefinition 对象,并将其转换为 RootBeanDefinition 对象
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//检查给定的合并的 BeanDefinition
checkMergedBeanDefinition(mbd, beanName, args);

// Guarantee initialization of beans that the current bean depends on.
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 + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}

// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
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;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

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);
}
beanInstance = 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);
}
});
beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new ScopeNotActiveException(beanName, scopeName, ex);
}
}
}
catch (BeansException ex) {
beanCreation.tag("exception", ex.getClass().toString());
beanCreation.tag("message", String.valueOf(ex.getMessage()));
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
finally {
beanCreation.end();
}
}

return adaptBeanInstance(name, beanInstance, requiredType);
}

Spring bean 的加载过程

  1. 从缓存中获取 bean
  2. 创建 bean 实例对象
  3. 从 bean 实例中获取对象

Bean 的生命周期

Spring Bean 实例化过程中提到 Spring 并不是一启动容器就开启 bean 的实例化进程,只有当客户端通过显示或者隐式的方式调用 BeanFactory 的 #getBean(...) 方法来请求某个实例对象的时候,它才会触发相应 bean 的实例化进程。当然,也可以选择直接使用 ApplicationContext 容器,因为该容器启动的时候会立刻调用注册到该容器所有 bean 定义的实例化方法。当然,对于 BeanFactory 容器而言,并不是所有的 #getBean(...) 方法都会触发实例化进程,比如 singleton 类型的 bean,该类型的 bean 只会在第一次调用 getBean() 的时候才会触发,而后续的调用则会直接返回容器缓存中的实例对象。

#getBean(...) 方法,只是 bean 实例化进程的入口,真正的实现逻辑其实是在 AbstractAutowireCapableBeanFactory 的 #doCreateBean(...) 中实现

img

bean 实例化

#doCreateBean(...) 方法中,首先进行 bean 实例化工作

这个时候的 Bean 还不能够被我们使用,连最基本的属性都没有设置

在实例化 bean 过程中,Spring 采用“策略模式”来决定采用哪种方式来实例化 bean,一般有反射和 CGLIB 动态字节码两种方式。

激活 Aware

完成 bean 对象实例化并且设置完相关属性和依赖后,则会开始 bean 的初始化进程( #initializeBean(...) ),初始化第一个阶段是检查当前 bean 对象是否实现了一系列以 Aware 结尾的的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}

BeanPostProcessor

对 Spring 容器提供的 bean 实例对象进行有效的扩展 增强处理

前置处理和后置处理

InitializingBean 和 init-method

在 bean 的初始化进程中会判断当前 bean 是否实现了 InitializingBean,如果实现了则调用 #afterPropertiesSet() 方法,进行初始化工作。然后再检查是否也指定了 init-method ,如果指定了则通过反射机制调用指定的 init-method 方法。

DisposableBean 和 destroy-method

与 InitializingBean 和 init-method 用于对象的自定义初始化工作相似,DisposableBean和 destroy-method 则用于对象的自定义销毁工作。

当一个 bean 对象经历了实例化、设置属性、初始化阶段,那么该 bean 对象就可以供容器使用了(调用的过程)。当完成调用后,如果是 singleton 类型的 bean ,则会看当前 bean 是否应实现了 DisposableBean 接口或者配置了 destroy-method 属性,如果是的话,则会为该实例注册一个用于对象销毁的回调方法,便于在这些 singleton 类型的 bean 对象销毁之前执行销毁逻辑。

但是,并不是对象完成调用后就会立刻执行销毁方法,因为这个时候 Spring 容器还处于运行阶段,只有当 Spring 容器关闭的时候才会去调用。但是, Spring 容器不会这么聪明会自动去调用这些销毁方法,而是需要我们主动去告知 Spring 容器。

  • 对于 BeanFactory 容器而言,我们需要主动调用 #destroySingletons() 方法,通知 BeanFactory 容器去执行相应的销毁方法。
  • 对于 ApplicationContext 容器而言,调用 #registerShutdownHook() 方法。

ApplicationContext 架构

ApplicationContext 结构类图

  1. 继承 MessageSource,提供国际化的标准访问策略。
  2. 继承 ApplicationEventPublisher ,提供强大的事件机制。
  3. 扩展 ResourceLoader,可以用来加载多个 Resource,可以灵活访问不同的资源。
  4. 对 Web 应用的支持。

ApplicationContext子接口

WebApplicationContext

1
2
3
4
5
6
7
// WebApplicationContext.java

public interface WebApplicationContext extends ApplicationContext {

ServletContext getServletContext();

}

该接口只有一个 #getServletContext() 方法,用于给 Servlet 提供上下文信息。

ConfigurableApplicationContext

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
// ConfigurableApplicationContext.java

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {

// 为 ApplicationContext 设置唯一 ID
void setId(String id);

// 为 ApplicationContext 设置 parent
// 父类不应该被修改:如果创建的对象不可用时,则应该在构造函数外部设置它
void setParent(@Nullable ApplicationContext parent);

// 设置 Environment
void setEnvironment(ConfigurableEnvironment environment);

// 获取 Environment
@Override
ConfigurableEnvironment getEnvironment();

// 添加 BeanFactoryPostProcessor
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);

// 添加 ApplicationListener
void addApplicationListener(ApplicationListener<?> listener);

// 添加 ProtocolResolver
void addProtocolResolver(ProtocolResolver resolver);

// 加载或者刷新配置
// 这是一个非常重要的方法
void refresh() throws BeansException, IllegalStateException;

// 注册 shutdown hook
void registerShutdownHook();

// 关闭 ApplicationContext
@Override
void close();

// ApplicationContext 是否处于激活状态
boolean isActive();

// 获取当前上下文的 BeanFactory
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

}

ConfigurableApplicationContext 接口提供的方法都是对 ApplicationContext 进行配置的

ConfigurableWebApplicationContext

该接口将这两个接口进行合并

ClassPathXmlApplicationContext

1
2
3
// 示例
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService studentService = (StudentService)ac.getBean("studentService");
1
2
3
4
5
org.springframework.context.support.AbstractApplicationContext
org.springframework.context.support.AbstractRefreshableApplicationContext
org.springframework.context.support.AbstractRefreshableConfigApplicationContext
org.springframework.context.support.AbstractXmlApplicationContext
org.springframework.context.support.ClassPathXmlApplicationContext

这种设计是模板方法模式典型的应用,AbstractApplicationContext 实现了 ConfigurableApplicationContext 这个全家桶接口,其子类 AbstractRefreshableConfigApplicationContext 又实现了 BeanNameAware 和 InitializingBean 接口。所以 ClassPathXmlApplicationContext 设计的顶级接口有:

1
2
3
4
5
6
7
8
9
BeanFactory:Spring 容器 Bean 的管理
MessageSource:管理 message ,实现国际化等功能
ApplicationEventPublisher:事件发布
ResourcePatternResolver:资源加载
EnvironmentCapable:系统 Environment(profile + Properties) 相关
Lifecycle:管理生命周期
Closable:关闭,释放资源
InitializingBean:自定义初始化
BeanNameAware:设置 beanName 的 Aware 接口

Spring 真的是一个非常优秀的框架,具有良好的结构设计和接口抽象,它的每一个接口职能单一,且都是具体功能到各个模块的高度抽象,且几乎每套接口都提供了一个默认的实现(defaultXXX)。

对于 ApplicationContext 体系而言,他继承 Spring 中众多的核心接口,能够为客户端提供一个相对完整的 Spring 容器,接口 ConfigurableApplicationContext 对 ApplicationContext 接口再次进行扩展,提供了生命周期的管理功能。
抽象类 ApplicationContext 对整套接口提供了大部分的默认实现,将其中“不易变动”的部分进行了封装,通过“组合”的方式将“容易变动”的功能委托给其他类来实现,同时利用模板方法模式将一些方法的实现开放出去由子类实现,从而实现“对扩展开放,对修改封闭”的设计原则。

ClassPathXmlApplicationContext 的类图

ApplicationContext 的 refresh()方法

1
void refresh() throws BeansException, IllegalStateException;

刷新 Spring 的应用上下文

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
// AbstractApplicationContext.java

@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备刷新上下文环境
prepareRefresh();

// 创建并初始化 BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// 填充BeanFactory功能
prepareBeanFactory(beanFactory);

try {
// 提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
postProcessBeanFactory(beanFactory);

// 激活各种BeanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);

// 注册拦截Bean创建的Bean处理器,即注册 BeanPostProcessor
registerBeanPostProcessors(beanFactory);

// 初始化上下文中的资源文件,如国际化文件的处理等
initMessageSource();

// 初始化上下文事件广播器
initApplicationEventMulticaster();

// 给子类扩展初始化其他Bean
onRefresh();

// 在所有bean中查找listener bean,然后注册到广播器中
registerListeners();

// 初始化剩下的单例Bean(非延迟加载的)
finishBeanFactoryInitialization(beanFactory);

// 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

// 销毁已经创建的Bean
destroyBeans();

// 重置容器激活标签
cancelRefresh(ex);

// 抛出异常
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

AOP

Transaction

IOC流程图

  1. Bean 的转换过程

下面这张图演示了一个可用的 bean 是如何从 xml 配置文件中演变过来的。

img

  1. ApplicationContext 的架构图

img

  1. load BeanDefinition 的全流程

img

  1. get Bean 的全流程

img