简

人生短暂,学海无边,而大道至简。


  • 首页

  • 归档

  • 分类

  • 标签

Spring源码分析—循环依赖处理

发表于 2019-01-25 | 分类于 spring

循环依赖,其实就是循环引用,就是两个或者两个以上的 bean 互相引用对方,最终形成一个闭环,如 A 依赖 B,B 依赖 C,C 依赖 A。

Spring 循环依赖的场景有两种:

构造器的循环依赖。
field 属性的循环依赖。

对于构造器的循环依赖,Spring 是无法解决的,只能抛出 BeanCurrentlyInCreationException 异常表示循环依赖。Spring 只解决 scope 为 singleton 的循环依赖。对于scope 为 prototype 的 bean ,Spring 无法解决,直接抛出 BeanCurrentlyInCreationException 异常。

getSingleton

spring加载 bean 最初始的方法 AbstractBeanFactory 的 doGetBean(final String name, final Class requiredType, final Object[] args, boolean typeCheckOnly) 方法中,首先会根据 beanName 从单例 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
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从map中获取bean如果不为空直接返回,不再进行初始化工作
Object singletonObject = this.singletonObjects.get(beanName);
// 缓存中的 bean 为空,且当前 bean 正在创建
// isSingletonCurrentlyInCreation(String beanName) 方法:
// 判断当前 singleton bean 是否处于创建中。bean 处于创建中,也就是说 bean 在初始化但是没有完成初始化,核心就在于提前曝光 bean 。
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 从 earlySingletonObjects 获取
singletonObject = this.earlySingletonObjects.get(beanName);
// earlySingletonObjects 中没有,且允许提前创建
// allowEarlyReference 是否允许从 singletonFactories 缓存中通过 #getObject() 方法,拿到对象。
// singletonFactories 才是 Spring 解决 singleton bean 的核心。
if (singletonObject == null && allowEarlyReference) {
// 从 singletonFactories 中获取对应的 ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 获得 bean
singletonObject = singletonFactory.getObject();
// 添加 bean 到 earlySingletonObjects 中
this.earlySingletonObjects.put(beanName, singletonObject);
// 从 singletonFactories 中移除对应的 ObjectFactory
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

这个方法主要是从三个缓存中获取,分别是:singletonObjects、earlySingletonObjects、singletonFactories 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//用于存放完全初始化好的 bean从该缓存中取出的 bean可以直接使用
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

/**
* 存放bean工厂对象
*
* 对应关系也是 bean name --> bean instance。
*
* singletonFactories中存放的 bean 不一定是完整的。
*
* bean 在创建过程中就已经加入到 singletonFactories 中了,
* 所以当在 bean 的创建过程中就可以通过 getBean() 方法获取。也是解决循环依赖的关键。
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

//存放的是 ObjectFactory 的映射,原始的bean对象用于解决循环依赖,注意:存到里面的对象还没有被填充属性
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
singletonObjects :单例对象的 Cache 。
singletonFactories : 单例对象工厂的 Cache 。
earlySingletonObjects :提前曝光的单例对象的 Cache 。

第一级为 singletonObjects
第二级为 earlySingletonObjects
第三级为 singletonFactories

流程:

首先,从一级缓存 singletonObjects 获取,没有且当前指定的 beanName 正在创建,就再从二级缓存 earlySingletonObjects 中获取。

如果,还是没有获取到且允许 singletonFactories 通过 #getObject() 获取,则从三级缓存 singletonFactories 获取。如果获取到,则通过其 #getObject() 方法,获取对象,并将其加入到二级缓存 earlySingletonObjects 中,并从三级缓存 singletonFactories 删除。就从三级缓存升级到二级缓存了。

所以,二级缓存存在的意义,就是缓存三级缓存中的 ObjectFactory 的 #getObject() 方法的执行结果,提早曝光的单例 Bean 对象。

缓存中的数据

缓存中的数据从哪里添加进来,在 AbstractAutowireCapableBeanFactory 的 #doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) 方法中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 解决循环依赖问题
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// Bean 满足三个条件时:
// 单例模式
// 运行循环依赖
// 当前单例 bean 是否正在被创建
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//提前将创建的 bean 实例加入到 singletonFactories 中
//当正在创建A时,A依赖B,此时通过(B将A作为ObjectFactory放入单例工厂中进行early expose,此处B需要引用A,但A正在创建,从单例工厂拿到ObjectFactory,从而允许循环依赖)
//避免后期循环依赖
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

当三级缓存 singletonFactories 和 二级缓存 earlySingletonObjects 中的值都有,在类 DefaultSingletonBeanRegistry 中的addSingleton(String beanName, Object singletonObject) 方法。

1
2
3
4
5
6
7
8
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}

添加至一级缓存,同时从二级、三级缓存中删除。

至此:Spring 在创建 bean 的时候并不是等它完全完成,而是在创建过程中将创建中的 bean 的 ObjectFactory 提前曝光(即加入到 singletonFactories 缓存中)。
这样,一旦下一个 bean 创建的时候需要依赖 bean ,则直接使用 ObjectFactory 的 #getObject() 方法来获取了。

分布式事务理论

发表于 2019-01-23 | 分类于 分布式

一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:

  • 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
  • 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。

并非任意的对数据库的操作序列都是数据库事务。数据库事务拥有以下四个特性,习惯上被称之为 ACID特性。

  • 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
  • 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
  • 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
  • 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。

对于分布式系统而言,要保证分布式系统中的数据一致性就需要一种方案,可以保证数据在子系统中始终保持一致,避免业务出现问题。这种实现方案就叫做分布式事务,要么一起成功,要么一起失败,必须是一个整体性的事务。

分布式中数据设计需要遵循的理论基础:CAP 理论和 BASE 理论。

CAP 理论

1
2
3
4
CAP,Consistency Availability Partition tolerance 的简写:
Consistency:一致性,对某个客户端来说,读操作能够返回最新的写操作结果。
Availability:可用性,非故障节点在合理的时间内返回合理的响应。
Partition tolerance:分区容错性,当出现网络分区后,系统能够继续提供服务,你知道什么是网络分区吗?

因为分布式系统中系统肯定部署在多台机器上,无法保证网络做到 100% 的可靠,所以 P 一定存在。

在出现网络分区后,必须要在这两者之间进行取舍,因此就有了两种架构:CP 架构 和 AP 架构

  • CP架构:违背了可用性的要求,只满足一致性和分区容错,即 CP,CAP 理论是忽略网络延迟,从系统 A 同步数据到系统 B 的网络延迟是忽略的。CP 架构保证了客户端在获取数据时一定是最近的写操作,或者获取到异常信息,绝不会出现数据不一致的情况。
  • AP 架构:违背了一致性的要求,只满足可用性和分区容错,即 AP,AP 架构保证了客户端在获取数据时无论返回的是最新值还是旧值,系统一定是可用的。CAP 理论关注粒度是数据,而不是整体系统设计的策略

BASE 理论

BASE 理论指的是基本可用 Basically Available,软状态 Soft State,最终一致性 Eventual Consistency,核心思想是即便无法做到强一致性,但应该采用适合的方式保证最终一致性。

BASE,Basically Available Soft State Eventual Consistency 的简写:

1
2
3
BA:Basically Available 基本可用,分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。
S:Soft State 软状态,允许系统存在中间状态,而该中间状态不会影响系统整体可用性。
E:Consistency 最终一致性,系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。

BASE 理论本质上是对 CAP 理论的延伸,是对 CAP 中 AP 方案的一个补充。

分布式事务协议

目前较为流行的分布式事务解决方案可以分为几种:

两阶段提交 X/Open XA 协议

XA 是一个分布式事务协议,由 Tuxedo 提出。XA 规范主要定义了(全局)事务管理器(Transaction Manager)和(局部)资源管理器(Resource Manager)之间的接口。XA 接口是双向的系统接口,在事务管理器(Transaction Manager)以及一个或多个资源管理器(Resource Manager)之间形成通信桥梁。

20200423150038

XA 协议采用两阶段提交方式来管理分布式事务。XA 接口提供资源管理器与事务管理器之间进行通信的标准接口。

1
XA协议比较简单,而且一旦商业数据库实现了XA协议,使用分布式事务的成本也比较低。但是,XA也有致命的缺点,那就是性能不理想,特别是在交易下单链路,往往并发量很高,XA无法满足高并发场景。XA目前在商业数据库支持的比较理想,在MySQL数据库中支持的不太理想,mysql的XA实现,没有记录prepare阶段日志,主备切换回导致主库与备库数据不一致。许多nosql也没有支持XA,这让XA的应用场景变得非常狭隘。

2PC:二阶段提交协议

二阶段提交(Two-phase Commit),是指,为了使基于分布式系统架构下的所有节点在进行事务提交时保持一致性而设计的一种算法(Algorithm)。通常,二阶段提交也被称为是一种协议(Protocol)。

在分布式系统中,每个节点虽然可以知晓自己的操作是成功或者失败,却无法知道其他节点的操作是成功或失败。

当一个事务跨越多个节点时,为了保持事务的 ACID 特性,需要引入一个作为协调者的组件来统一掌控所有节点(称作参与者)的操作结果并最终指示这些节点是否要把操作结果进行真正的提交(比如将更新后的数据写入磁盘等等)。

因此,二阶段提交的算法思路可以概括为:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。

1
2
3
4
5
6
7
8
投票阶段 Prepares:
协调者向所有参与者询问是否可以执行提交操作,并开始等待各参与者的响应。
参与者执行事务操作,如果执行成功就返回 Yes 响应,如果执行失败就返回 No 响应。
如果协调者接受参与者响应超时,也会认为执行事务操作失败。

提交阶段 Commit:
如果第一阶段汇总所有参与者都返回 Yes 响应,协调者向所有参与者发出提交请求,所有参与者提交事务。
如果第一阶段中有一个或者多个参与者返回 No 响应,协调者向所有参与者发出回滚请求,所有参与者进行回滚操作。

二阶段提交优点:尽量保证了数据的强一致,但不是 100% 一致。

二阶段提交缺点:

1
2
3
4
单点故障,由于协调者的重要性,一旦协调者发生故障,参与者会一直阻塞,尤其是在第二阶段,协调者发生故障,那么所有的参与者都处于锁定事务资源的状态中,而无法继续完成事务操作。
同步阻塞,由于所有节点在执行操作时都是同步阻塞的,当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
数据不一致,在第二阶段中,当协调者向参与者发送提交事务请求之后,发生了局部网络异常或者在发送提交事务请求过程中协调者发生了故障,这会导致只有一部分参与者接收到了提交事务请求。
而在这部分参与者接到提交事务请求之后就会执行提交事务操作。但是其他部分未接收到提交事务请求的参与者则无法提交事务。从而导致分布式系统中的数据不一致。

二阶段提交的问题:如果协调者在第二阶段发送提交请求之后挂掉,而唯一接受到这条消息的参与者执行之后也挂掉了,即使协调者通过选举协议产生了新的协调者并通知其他参与者进行提交或回滚操作的话,都可能会与这个已经执行的参与者执行的操作不一样。

当这个挂掉的参与者恢复之后,就会产生数据不一致的问题。

3PC:三阶段提交协议

三阶段提交(Three-phase commit),是为解决两阶段提交协议的缺点而设计的。与两阶段提交不同的是,三阶段提交是“非阻塞”协议。

三阶段提交在两阶段提交的第一阶段与第二阶段之间插入了一个准备阶段,使得原先在两阶段提交中,参与者在投票之后,由于协调者发生崩溃或错误,而导致参与者处于无法知晓是否提交或者中止的“不确定状态”所产生的可能相当长的延时的问题得以解决。

三阶段提交的三个阶段:CanCommit/PreCommit/DoCommit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
询问阶段:CanCommit
协调者向参与者发送 Commit 请求,参与者如果可以提交就返回 Yes 响应,否则返回 No 响应。

准备阶段:PreCommit
协调者根据参与者在询问阶段的响应判断是否执行事务还是中断事务:
如果所有参与者都返回 Yes,则执行事务。
如果参与者有一个或多个参与者返回 No 或者超时,则中断事务。

参与者执行完操作之后返回 ACK 响应,同时开始等待最终指令。

③提交阶段:DoCommit
协调者根据参与者在准备阶段的响应判断是否执行事务还是中断事务:
如果所有参与者都返回正确的 ACK 响应,则提交事务。
如果参与者有一个或多个参与者收到错误的 ACK 响应或者超时,则中断事务。
如果参与者无法及时接收到来自协调者的提交或者中断事务请求时,会在等待超时之后,会继续进行事务提交。

协调者收到所有参与者的 ACK 响应,完成事务。

在三阶段提交中,如果在第三阶段协调者发送提交请求之后挂掉,并且唯一的接受的参与者执行提交操作之后也挂掉了,这时协调者通过选举协议产生了新的协调者。

在二阶段提交时存在的问题就是新的协调者不确定已经执行过事务的参与者是执行的提交事务还是中断事务。

但是在三阶段提交时,肯定得到了第二阶段的再次确认,那么第二阶段必然是已经正确的执行了事务操作,只等待提交事务了。

所以新的协调者可以从第二阶段中分析出应该执行的操作,进行提交或者中断事务操作,这样即使挂掉的参与者恢复过来,数据也是一致的。

所以,三阶段提交解决了二阶段提交中存在的由于协调者和参与者同时挂掉可能导致的数据一致性问题和单点故障问题,并减少阻塞。因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行提交事务,而不会一直持有事务资源并处于阻塞状态。

三阶段提交的问题:在提交阶段如果发送的是中断事务请求,但是由于网络问题,导致部分参与者没有接到请求。那么参与者会在等待超时之后执行提交事务操作,这样这些由于网络问题导致提交事务的参与者的数据就与接受到中断事务请求的参与者存在数据不一致的问题。

强一致性分布式事务

基于 2PC/XA 协议实现的 JTA

1
2
3
Transaction Manager:常用方法,可以开启,回滚,获取事务。begin(),rollback()...
XAResouce:资源管理,通过 Session 来进行事务管理,commit(xid)...
XID : 每一个事务都分配一个特定的 XID。

JTA 主要的原理是二阶段提交,当整个业务完成了之后只是第一阶段提交,在第二阶段提交之前会检查其他所有事务是否已经提交。

如果前面出现了错误或是没有提交,那么第二阶段就不会提交,而是直接回滚,这样所有的事务都会做回滚操作。基于 JTA 这种方案实现分布式事务的强一致性。

JTA 的特点:基于两阶段提交,有可能会出现数据不一致的情况;事务时间过长,阻塞;性能低,吞吐量低

最终一致性分布式事务方案

JTA 方案适用于单体架构多数据源时实现分布式事务,但对于微服务间的分布式事务就无能为力了,我们需要使用其他的方案实现分布式事务。

本地消息表

本地消息表的核心思想是将分布式事务拆分成本地事务进行处理。

以本文中例子,在订单系统新增一条消息表,将新增订单和新增消息放到一个事务里完成,然后通过轮询的方式去查询消息表,将消息推送到 MQ,库存系统去消费 MQ。

消息事务+最终一致性

基于消息中间件的两阶段提交,本质上是对消息中间件的一种特殊利用,它是将本地事务和发消息放在了一个分布式事务里,保证要么本地操作成功成功并且对外发消息成功,要么两者都失败,开源的RocketMQ就支持这一特性。具体原理如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1、A系统向消息中间件发送一条预备消息
2、消息中间件保存预备消息并返回成功
3、A执行本地事务
4、A发送提交消息给消息中间件

通过以上4步完成了一个消息事务。对于以上的4个步骤,每个步骤都可能产生错误:

步骤一出错,则整个事务失败,不会执行A的本地操作
步骤二出错,则整个事务失败,不会执行A的本地操作
步骤三出错,这时候需要回滚预备消息,怎么回滚?答案是A系统实现一个消息中间件的回调接口,消息中间件会去不断执行回调接口,检查A事务执行是否执行成功,如果失败则回滚预备消息
步骤四出错,这时候A的本地事务是成功的,那么消息中间件要回滚A吗?答案是不需要,其实通过回调接口,消息中间件能够检查到A执行成功了,这时候其实不需要A发提交消息了,消息中间件可以自己对消息进行提交,从而完成整个消息事务
基于消息中间件的两阶段提交往往用在高并发场景下,将一个分布式事务拆成一个消息事务(A系统的本地操作+发消息)+B系统的本地操作,其中B系统的操作由消息驱动,只要消息事务成功,那么A操作一定成功,消息也一定发出来了,这时候B会收到消息去执行本地操作,如果本地操作失败,消息会重投,直到B操作成功,这样就变相地实现了A与B的分布式事务。

此方案能够完成A和B的操作,但是A和B并不是严格一致的,而是最终一致的,我们在这里牺牲了一致性,换来了性能的大幅度提升。当然,这种玩法也是有风险的,如果B一直执行不成功,那么一致性会被破坏,具体要不要玩,还是得看业务能够承担多少风险。

TCC

TCC(Try、Confirm、Cancel)是两阶段提交的一个变种。TCC提供了一个框架,需要应用程序按照该框架编程,将业务逻辑的每个分支都分为Try、Confirm、Cancel三个操作集。TCC让应用程序自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。

以一个典型的淘宝订单为例,按照TCC框架,应用需要在Try阶段将商品的库存减去,将买家支付宝账户中的相应金额扣掉,在临时表中记录下商品的数量,订单的金额等信息;另外再编写Confirm的逻辑,即在临时表中删除相关记录,生成订单,告知CRM、物流等系统,等等;以及Cancel逻辑,即恢复库存和买家账户金额,删除临时表相关记录。

TCC就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,复杂度也不一样,因此,这种模式并不能很好地被复用。

FastDFS 分布式文件系统

发表于 2019-01-23 | 分类于 分布式
FastDFS介绍

FastDFS是用c语言编写的一款开源的分布式文件系统。FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS架构

20200423144936

Tracker 集群
 FastDFS集群中的Tracker server可以有多台,Trackerserver之间是相互平等关系同时提供服务,Trackerserver不存在单点故障。客户端请求Trackerserver采用轮询方式,如果请求的tracker无法提供服务则换另一个tracker。

** Storage集群**
 Storage集群采用了分组存储方式。storage集群由一个或多个组构成,集群存储总容量为集群中所有组的存储容量之和。一个组由一台或多台存储服务器组成,组内的Storage server之间是平等关系,不同组的Storageserver之间不会相互通信,同组内的Storageserver之间会相互连接进行文件同步,从而保证同组内每个storage上的文件完全一致的。一个组的存储容量为该组内存储服务器容量最小的那个,由此可见组内存储服务器的软硬件配置最好是一致的。
 采用分组存储方式的好处是灵活、可控性较强。比如上传文件时,可以由客户端直接指定上传到的组也可以由tracker进行调度选择。一个分组的存储服务器访问压力较大时,可以在该组增加存储服务器来扩充服务能力(纵向扩容)。当系统容量不足时,可以增加组来扩充存储容量(横向扩容)。

Storage状态收集
 Storage server会连接集群中所有的Tracker server,定时向他们报告自己的状态,包括磁盘剩余空间、文件同步状况、文件上传下载次数等统计信息。

文件上传流程

20200423144950

客户端上传文件后存储服务器将文件ID返回给客户端,此文件ID用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。
http://static.****.com/group1/M00/00/00/CgEBDl0teZaAHJ-fAAJ6QvNftQM767.png

  • 组名:文件上传后所在的storage组名称,在文件上传成功后有storage服务器返回,需要客户端自行保存。
  • 虚拟磁盘路径:storage配置的虚拟路径,与磁盘选项store_path*对应。如果配置了store_path0则是M00,如果配置了store_path1则是M01,以此类推。
  • 数据两级目录:storage服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据文件。
  • 文件名:与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储服务器IP地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。

文件下载流程

20200423145000

tracker根据请求的文件路径即文件ID 来快速定义文件。
1.通过组名tracker能够很快的定位到客户端需要访问的存储服务器组是group1,并选择合适的存储服务器提供客户端访问。
2.存储服务器根据“文件存储虚拟磁盘路径”和“数据文件两级目录”可以很快定位到文件所在目录,并根据文件名找到客户端需要访问的文件。

FastDFS安装

搭建很繁琐,通常由运维来操作,这里不介绍,如果自己demo,可以直接拿搭好的Docker镜像 。

FastDFS使用

见项目 https://github.com/huingsn/tech-point-record FastDFS-demo

在spring boot2中使用redis作为缓存

发表于 2019-01-23 | 分类于 springboot

1、简单的一个demo

spring boot2.0中使用Redis做为缓存,首选引入 依赖:

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.hu</groupId>
<artifactId>data-cache-redis-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>

</project>

配置文件:

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
spring:
datasource:
#使用druid数据源
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://172.16.3.34:3306/fenxiao_test?useUnicode=true&characterEncoding=utf-8
username: root
password: root
initialSize: 1
minIdle: 3
maxActive: 20
#配置获取连接等待超时的时间
maxWait: 6000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
#配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 30000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
#配置监控统计拦截的filters去掉后监控界面sql无法统计wall用于防火墙
filters: stat,wall,slf4j
#通过connectProperties属性来打开mergeSql功能,慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
useGlobalDataSourceStat: true

cache:
type: redis
#redis
redis:
host: 172.16.3.34
port: 6379
password:
timeout: 100000

mybatis:
mapper-locations: classpath:mybatis/*/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

构建数据源和缓存配置

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
/**
* 构建数据源对象
*/
@ConditionalOnClass({EnableTransactionManagement.class})
@Configuration
public class DataSourceConfig {
private static Logger log = LoggerFactory.getLogger(DataSourceConfig.class);

@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid")
public DataSource druidDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
return druidDataSource;
}
}

/**
* 使用Redis做为缓存,和spring boot1.x不同
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

/*@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;*/

//自定义缓存key生成策略
@Bean
public KeyGenerator keyGenerator(){
return (o, method, params) ->{
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName()); // 类目
sb.append(method.getName()); // 方法名
for(Object param: params){
sb.append(param.toString()); // 参数名
}
return sb.toString();
};
}
//缓存管理器Spring boot2.X 配置
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) // 设置缓存有效期一小时
// 设置key的序列化方式
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value的序列化方式
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
// 不缓存null值
.disableCachingNullValues();

RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();

return redisCacheManager;
}
//以下是Spring boot1.X 配置
/*@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
//设置缓存过期时间
cacheManager.setDefaultExpiration(10000);
return cacheManager;
}*/
/*@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){
StringRedisTemplate template = new StringRedisTemplate(factory);
//设置序列化工具
setSerializer(template);
return template;
}*/
/* @SuppressWarnings("all")
private void setSerializer(StringRedisTemplate template){
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//查看Redis的Bean定义发现,对key的序列化使用的是StringRedisSerializer系列化,value值的序列化是GenericJackson2JsonRedisSerializer的序列化方法。
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
}*/
}

创建实体,mapper和service

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
public class Student implements Serializable {
private Long id;
private String name;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Student(String name) {
this.name = name;
}
public Student(){}
@Override
public String toString() {
return "\n{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

@Mapper
public interface StudentMapper {
@Insert("insert into Student(name) values(#{name})")
int insertStudent(Student student);

@Select("select * from Student")
List<Student> selectStudent();
}

/**
* @Cacheable(cacheNames = "student") 这样就使用了缓存
*
*/
@Service
public class StudentService {

@Autowired
StudentMapper studentMapper;

public int saveStudent(Student student){
return studentMapper.insertStudent(student);
}

@Cacheable(cacheNames = "student")
public List<Student> getStudents(){
return studentMapper.selectStudent();
}
}


测试类:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class Test {
@Autowired
StudentService studentService;

@org.junit.Test
public void stu(){

Random random = new Random();
Student student = new Student("小米"+random.nextInt(100));

//System.out.println(studentService.saveStudent(student));

System.out.println(Arrays.toString(studentService.getStudents().toArray()));
}
}

第一次运行如下:

20200423134656

可以看到由于缓存中没有数据,去数据库查询。并且Redis中有了数据:

20200423134738

第二次运行:

20200423134754

没有mybatis查询的日志,直接在redis中就有值存在。

io.lettuce.core.KqueueProvider是指Spring boot2中默认使用lettuce操作Redis

使用缓存的几种注解:

20200423134634

具体可以参考:缓存数据

@CacheConfig 每个缓存操作都要写cacheName,keygenerator,很麻烦,为了统一配置使用CacheConfig。

2、统一做缓存

第一种实现可以通过自定义注解,对读方法增加aop处理,同时对写方法做删除缓存处理
定义两个注解:

1
2
3
4
5
6
7
8
9
10
11
12
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ClearCache {
String[] keys(); //需要清除的key
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisCache {
Class type(); //用户存放类类型
}

切面:

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
/**
* 缓存切面
*/
@Aspect
@Component
public class RedisCacheAspect {
@Autowired
private RedisClient redisClient;

/**
* 方法调用前,先查询缓存。如果存在缓存,则返回缓存数据,如果没有缓存,查數據庫,然后将结果放到缓存中
*/
@Around("execution(public * com.hu.service.StudentService.getStudent(..))")
public Object cache(ProceedingJoinPoint jp) throws Throwable {
//获取被代理的方法
MethodSignature msig = (MethodSignature) jp.getSignature();
Object target = jp.getTarget();
Object[] args = jp.getArgs();
Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
String clazzName = target.getClass().getName();
//得到被代理方法上的注解
RedisCache annotation = currentMethod.getAnnotation(RedisCache.class);
//如果没有缓存注解直接放行,查询数据库
if(annotation==null){
return jp.proceed(args);
}
System.out.println("cache");
//有缓存注解,查询redis
//生成对应的key值
String key = genKey(clazzName, currentMethod.getName(), args);
//查询redis
Student student = (Student) redisClient.get(key);
//如果redis中有直接返回缓存中内容
if(student!=null){
return student;
}
//如果redis中没有,查询数据库
Object object=jp.proceed(args);
//如果数据库中查询不为空,将数据添加到reids中
if(object!=null){
redisClient.set(key,object);
}
return object;
}


/**
* 对于插入,在方法调用前清除缓存,然后调用业务方法
*/
@Around("execution(public * com.hu.service.StudentService.saveStudent(..))")
public Object clearCache(ProceedingJoinPoint jp) throws Throwable {
MethodSignature msig = (MethodSignature) jp.getSignature();
Object target = jp.getTarget();
Object[] args = jp.getArgs();
Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
String clazzName = target.getClass().getName();
//得到被代理方法上的注解
ClearCache annotation = currentMethod.getAnnotation(ClearCache.class);
String[] keys =annotation.keys();
System.out.println("clearCache");
if(keys!=null&&keys.length>0){
// 清除对应缓存
for(String key :keys){
redisClient.delete(key);
}
}
return jp.proceed(jp.getArgs());
}


/**
* 根据类名、方法名和参数生成key
*/
protected String genKey(String clazzName, String methodName, Object[] args) {
StringBuilder sb = new StringBuilder(clazzName);
sb.append(":");
sb.append(methodName);
for (Object obj : args) {
sb.append(":");
sb.append(obj.toString());
}

return sb.toString();
}
}

配置和操作Redis类:

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
@Configuration
@EnableAutoConfiguration
public class MyRedisTemplate {
@Bean
public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String, String> template = new StringRedisTemplate(factory);
//设置序列化工具
setSerializer(template);
return template;
}
@SuppressWarnings("all")
private void setSerializer(RedisTemplate template){
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
//查看Redis的Bean定义发现,对key的序列化使用的是StringRedisSerializer系列化,value值的序列化是GenericJackson2JsonRedisSerializer的序列化方法。
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
}
}


/**
* 操作方法
*/
@Component
public class RedisClient {

private final static Logger log = LoggerFactory.getLogger(RedisClient.class);

@Autowired
RedisTemplate redisTemplate;

/**
* 删除key
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void delete(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}

/**
* 获取key
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}

/**
* 存入key
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
log.info(e.getMessage());
return false;
}
}

/**
* key存放并设置时间
*
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
log.info(e.getMessage());
return false;
}
}

}

业务类:

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
@Service
public class StudentService {

@Autowired
StudentMapper studentMapper;

//清除对应的缓存
@ClearCache(keys={"com.hu.service.StudentService:saveStudent:10"})
public int saveStudent(Student student){
return studentMapper.insertStudent(student);
}

@RedisCache(type=Student.class)
public Student getStudent(Long id){
return studentMapper.selectStudent(id);
}
}

测试:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class Test {
@Autowired
StudentService studentService;

@org.junit.Test
public void stu(){

Random random = new Random();
Student student = new Student("小米"+random.nextInt(100));

//System.out.println(studentService.saveStudent(student));

System.out.println(studentService.getStudent(6L));
}
}

其他和上面例子一致

第一次运行:

20200423135004

可以看到查询了数据库,并且吧结果放到缓存。

20200423135027

第二次运行:可以看到直接从缓存取。

第二种方式是,不用注解,直接通过AOP统一为部分或者全部的方法添加缓存或者删除更新缓存。

代码详见:https://github.com/huingsn/tech-point-record中的data-cache-redis-demo

Spring Boot Maven plugin插件

发表于 2019-01-23 | 分类于 maven

Spring Boot的Maven插件(Spring Boot Maven plugin)能够以Maven的方式为应用提供Spring Boot的支持,即为Spring Boot应用提供了执行Maven操作的可能。

Spring Boot Maven plugin能够将Spring Boot应用打包为可执行的jar或war文件,然后以通常的方式运行Spring Boot应用。

Spring Boot Maven plugin中的五个Goals配置

spring-boot:repackage,默认goal。在mvn package之后,再次打包可执行的jar/war,同时保留mvn package生成的jar/war为.origin
spring-boot:run,运行Spring Boot应用
spring-boot:start,在mvn integration-test阶段,进行Spring Boot应用生命周期的管理
spring-boot:stop,在mvn integration-test阶段,进行Spring Boot应用生命周期的管理
spring-boot:build-info,生成Actuator使用的构建信息文件build-info.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>.....</mainClass>
<layout>JAR</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

最主要的goal是repackage,其在Maven的package生命周期阶段,能够将mvn package生成的软件包,再次打包为可执行的软件包,并将mvn package生成的软件包重命名为*.original。

比如执行命令:
mvn package spring-boot:repackage

会生成的两个jar文件,一个是.jar,另一个是.jar.original。

在执行上述命令的过程中,Maven首先在package阶段打包生成*.jar文件;然后执行spring-boot:repackage重新打包,查找Manifest文件中配置的Main-Class属性,如下所示:

Manifest-Version: 1.0
Implementation-Title: jinkao-distribution-web
Implementation-Version: 1.0-SNAPSHOT
Built-By: huajin
Implementation-Vendor-Id: com.jinkao
Spring-Boot-Version: 2.0.1.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.jinkao.distribution.ApplicationMain
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_202
Implementation-URL: https://projects.spring.io/spring-boot/#/spring-boot-starter-parent/jinkao-distribution/jinkao-distribution-web

其中的Main-Class属性值为org.springframework.boot.loader.JarLauncher;Start-Class属性值为应用入口。

Spring Boot Maven plugin会在打包过程中自动为Manifest文件设置Main-Class属性,事实上该属性究竟作用几何,还可以受Spring Boot Maven plugin的配置属性layout控制的,示例如下。

layout属性的值:

JAR,即通常的可执行jar
    Main-Class: org.springframework.boot.loader.JarLauncher
WAR,即通常的可执行war,需要的servlet容器依赖位于WEB-INF/lib-provided
    Main-Class: org.springframework.boot.loader.warLauncher
ZIP,即DIR,类似于JAR
    Main-Class: org.springframework.boot.loader.PropertiesLauncher
MODULE,将所有的依赖库打包(scope为provided的除外),但是不打包Spring Boot的任何Launcher。
NONE,将所有的依赖库打包,但是不打包Spring Boot的任何Launcher。

参考:https://docs.spring.io/spring-boot/docs/current/reference/html/build-tool-plugins-maven-plugin.html

在Spring boot中使用AOP实现多数据源读写分离

发表于 2019-01-23 | 分类于 springboot

1、建项目,引入依赖:

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.hu</groupId>
<artifactId>database-read-write-demo1</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>

2、项目配置:

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
# 主数据源,默认
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
master:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://172.16.3.34:3306/fenxiao_test?useUnicode=true&characterEncoding=utf-8
username: root
password: root
initialSize: 5
minIdle: 5
maxActive: 50
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20

# 从数据源
slave:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://172.16.3.34:3306/fenxiao_test?useUnicode=true&characterEncoding=utf-8
username: root
password: root
initialSize: 5
minIdle: 5
maxActive: 50
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20

3、数据源配置和注解及读写分离配置

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
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceType {
String value() default "";
}

/**
* 构建数据源对象
*/
@ConditionalOnClass({EnableTransactionManagement.class})
@Configuration
public class DataSourceConfig {
private static Logger log = LoggerFactory.getLogger(DataSourceConfig.class);

@Value("${spring.datasource.type}")
public Class<? extends DataSource> type;

@Bean(name = "masterDataSource", destroyMethod = "close", initMethod = "init")
@ConfigurationProperties(prefix = "spring.master")
public DataSource masterDataSource() {
log.info("************ masterDataSource init ************");
return DataSourceBuilder.create().type(type).build();
}

@Bean(name = "slaveDataSource", destroyMethod = "close", initMethod = "init")
@ConfigurationProperties(prefix = "spring.slave")
public DataSource slaveDataSource() {
log.info("************ slaveDataSource init ************");
return DataSourceBuilder.create().type(type).build();
}

/**
* 注册的bean放入一个map里面,后面就可以动态从这个map里面获取对应的数据源
*/
@Bean
public RoutingDataSource routingDataSource() {
RoutingDataSource routingDataSource = new RoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("write", masterDataSource());
dataSourceMap.put("read", slaveDataSource());
routingDataSource.setTargetDataSources(dataSourceMap);
//默认数据源
routingDataSource.setDefaultTargetDataSource(masterDataSource());
return routingDataSource;
}

@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
//这里直接写上面生成的Bean,不然@Value("${spring.datasource.type}")获取不到,就会走默认的连接池
sqlSessionFactoryBean.setDataSource(routingDataSource());
//sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/**/*.xml"));
sqlSessionFactoryBean.setTypeAliasesPackage("com.hu.entity");
sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
return sqlSessionFactoryBean.getObject();
}

}
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
/**
* 根据ThreadLocal来实现数据源的动态改变
*/
public class DataSourceContextHolder {
private static Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class);

private static ThreadLocal<String> datasourceContext = new ThreadLocal<>();

public static void switchDataSource(String datasource) {
log.info("数据源: {}", datasource);
datasourceContext.set(datasource);
}

public static String getDataSource() {
return datasourceContext.get();
}

public static void clear() {
datasourceContext.remove();
}
}

/**
* AbstractRoutingDataSource提供了程序运行时动态切换数据源的方法 *
* 在dao类或方法上标注需要访问数据源的关键字,路由到指定数据源,获取连接。
*
* 这里通过配置注解方式
*/
public class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}

/**
* 利用springAOP对方法的切入,在方法执行前判断使用哪个数据源
*/
@Aspect
@Component
public class DataSourceAop {
private static Logger log = LoggerFactory.getLogger(DataSourceAop.class);

@Pointcut("@annotation(com.hu.aop.DataSourceType)")
public void cutMethod() {
}

@Before("cutMethod()")
public void beforeRead(JoinPoint point) {
String className = point.getTarget().getClass().getName();
String methodName = point.getSignature().getName();
log.info("执行" + className + "." + methodName + "()方法");
Method method = ((MethodSignature) point.getSignature()).getMethod();
DataSourceType annotation = method.getAnnotation(DataSourceType.class);
if (null == annotation) {
annotation = point.getTarget().getClass().getAnnotation(DataSourceType.class); }
if (null != annotation) {
// 切换数据源
DataSourceContextHolder.switchDataSource(annotation.value());
}
}
@After("cutMethod()")
public void afterExecute() {
DataSourceContextHolder.clear();
}
}

4、业务demo

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
public class Student implements Serializable {
private Long id;
private String name;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Student(String name) {
this.name = name;
}
public Student(){}
@Override
public String toString() {
return "\n{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

@Mapper
public interface StudentMapper {
@Insert("insert into Student(name) values(#{name})")
int insertStudent(Student student);

@Select("select * from Student")
List<Student> selectStudent();
}


@Service
public class StudentService {

@Autowired
StudentMapper studentMapper;

@DataSourceType("write")
public int saveStudent(Student student){
return studentMapper.insertStudent(student);
}

@DataSourceType("read")
@Transactional(propagation= Propagation.REQUIRED,isolation= Isolation.DEFAULT,readOnly=true)
public List<Student> getStudents(){
return studentMapper.selectStudent();
}
}

5、测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RunWith(SpringRunner.class)
@SpringBootTest(classes = APP.class)
public class Test {
@Autowired
StudentService studentService;

@org.junit.Test
public void stu(){
Random random = new Random();
Student student = new Student("小米"+random.nextInt(100));

System.out.println(studentService.saveStudent(student));

System.out.println(Arrays.toString(studentService.getStudents().toArray()));
}
}

结果:
数据源初始化

20200423140011

测试执行结果:

20200423135940

demo完成,还可以增加多个读库数据源,详见代码
https://github.com/huingsn/tech-point-record中的 database-read-write-demo1和database-read-write-demo

spring5日志框架-常用日志框架使用介绍

发表于 2019-01-22 | 分类于 spring

Java的Log技术

概念理解

1
2
日志门面:一般采取facade设计模式(外观设计模式:外观模式定义了一个高层的功能,为子系统中的多个模块协同的完成某种功能需求提供简单的对外功能调用方式,使得这一子系统更加容易被外部使用)设计的一组接口应用。
日志实现:接口的实现

日志实现底层基本组成如下:

1
2
3
Loggers:Logger负责捕捉事件并将其发送给合适的Appender。
Appenders:也被称为Handlers,负责从Logger中取出日志消息并将消息发送出去,比如发送到控制台、文件、网络上的其他日志服务或操作系统日志等
Layouts:也被称为Formatters,它负责对日志事件中的数据进行转换和格式化。Layouts决定了数据在一条日志记录中的最终形式。

实现:当Logger记录一个事件时,它将事件转发给适当的Appender。然后Appender使用Layout来对日志记录进行格式化,并将其发送给控制台、文件或者其它目标位置。另外,Filters可以让你进一步指定一个Appender是否可以应用在一条特定的日志记录上。在日志配置中,Filters并不是必需的,但可以让你更灵活地控制日志消息的流动。

20200420223637

主流的Log技术

日志门面 commons-logging,slf4j

日志实现 log4j,jdk-logging,logback,log4j2

这也符合Java的面向对象设计理念,将接口与实现相分离。

日志门面系统的出现其实已经很大程度上缓解了日志系统的混乱,很多库的作者也已经意识到了日志门面系统的重要性,不在库中直接使用具体的日志实现框架。slf4j作为现代的日志门面系统,已经成为事实的标准,并且为其他日志系统做了十足的兼容工作。

我们能做的就是选一个日志实现框架。logback,log4j2是现代的高性能日志实现框架

20200420223741

log4j 和 log4j2

log4j是Apache的一个开源项目,log4j2和log4j是一个作者,只不过log4j2是重新架构的一款日志组件,他抛弃了之前log4j的不足,以及吸取了优秀的logback的设计重新推出的一款新组件。

log4j是通过一个.properties的文件作为主配置文件的,而现在的log4j 2则已经弃用了这种方式,采用的是.xml,.json或者.jsn这种方式来做,可能这也是技术发展的一个必然性,毕竟properties文件的可阅读性真的是有点差。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<!-- log4j 2则是需要2个核心 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.5</version>
</dependency>

不需要再依赖第三方的技术

JCL(Jakarta Commons Logging)

是apache公司开发的一个抽象日志通用框架,本身不实现日志记录,但是提供了记录日志的抽象方法即接口(info,debug,error…….)。

1
2
3
4
5
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.2</version>
</dependency>

JCL不直接记录日志,通过第三方记录日志:如果没有log4j的依赖情况下用JUL(JUL是jdk自带的日志实现)。如果有了log4j则使用log4j。

通过查看源码得知,底层通过一个数组存放具体的日志框架的类名,然后循环数组依次去匹配这些类名是否在程序中被依赖了,如果找到被依赖的则直接使用。默认有四种:

1
2
3
4
5
classesToDiscover = {String[4]@513} 
3 = "org.apache.commons.logging.impl.SimpleLog"
2 = "org.apache.commons.logging.impl.Jdk13LumberjackLogger"
1 = "org.apache.commons.logging.impl.Jdk14Logger"
0 = "org.apache.commons.logging.impl.Log4JLogger"

循环加载类 获取Class:

20200420223807

成功过的到直接返回Class

20200420223820

JUL

java自带的一个日志记录的技术,直接使用。

1
2
3
4
5
6
public static void main(String[] args) throws IOException {
log.info("info"); //信息日志
log.warning("warning"); //警告日志
log.log(Level.SEVERE,"server"); //严重日志
log.fine("fine");
}

JUL日志等级划分(优先级递减)及内置代表的整数如下:

1
2
3
4
5
6
7
8
9
OFF(Integer.MAX_VALUE)
SEVERE(1000)
WARNING(900)
INFO(800)
CONFIG(700)
FINE(500)
FINER(400)
FINEST(300)
ALL(Integer.MIN_VALUE)

当为 Logger 指定了一个 Level, 该 Logger 会包含当前指定级别以及更高级别的日志,logger默认的级别是INFO,比INFO更低的日志将不显示。JUL的默认配置文件loging.properties,该配置文件位于jdk安装目录的lib包下。

如果要更改logger的输出等级,可以通过修改对应配置项的level等级,也可以通过代码的方式去动态设置logger的等级。

1
log.setLevel(Level.ALL);//设置logger的日志级别为全部,默认输出所有级别日志信息

Handler

JUL中用的比较多的是两个Handler类:ConsoleHandler和FileHandler,其中,ConsoleHandler是对控制台输出的默认处理类,FileHandler是对文件输出的默认处理类

1
2
3
4
5
6
7
8
9
10
11
log.setUseParentHandlers(false); //禁用日志原本处理类
ConsoleHandler consoleHandler = new ConsoleHandler(); //创建控制台输出控制Handler
consoleHandler.setLevel(Level.INFO); //设置控制台输出级别
log.addHandler(consoleHandler); //将Handler加入logger中

//文件输出日志方式
log.setUseParentHandlers(false); //禁用日志原本处理类

FileHandler fileHandler = new FileHandler("日志路径/testlog.log");
fileHandler.setLevel(Level.ALL); //记录级别
log.addHandler(fileHandler); //添加Handler

如果没有 log.setUseParentHandlers(false); 父Handler与子Handler都会生效,此时会输出两遍日志内容

自定义Handler

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
public class MyHandler extends Handler {

private LogRecord record;

@Override
public void publish(LogRecord record) {
this.record = record;
//自定义内容
}

@Override
public void flush() {
System.out.println("logger:"+this.record.getLoggerName()+"flush");
}

@Override
public void close() throws SecurityException {
System.out.println("logger:"+this.record.getLoggerName()+"close");
}
}


//测试
public static void main(String[] args) throws IOException {

log.setUseParentHandlers(false); //禁用日志原本处理类

MyHandler myHandler = new MyHandler(); //创建自定义日志处理类实体
log.addHandler(myHandler); //添加日志处理实体类

//...
}

Formatter
默认输出是xml格式,修改显示方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyFormate extends Formatter {
@Override
public String format(LogRecord record) {
return new Date()+"-["+record.getSourceClassName()+"."+record.getSourceMethodName()+"]"+record.getLevel()+":"+record.getMessage()+"\n";
}
}


man方法中修改为:
FileHandler fileHandler = new FileHandler("日志路径/testlog.log");
fileHandler.setLevel(Level.ALL); //记录级别
fileHandler.setFormatter(new MyFormate()); //设置自定义样式
log.addHandler(fileHandler); //添加Handler
slf4j

slf4j不记录日志,通过绑定器绑定一个具体的日志记录来完成日志记录。slf4j是门面模式的典型应用

引入包:

1
2
3
4
5
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>

测试运行

1
2
3
4
5
6
public class SLF4j {
public static void main(String[] args){
Logger log = LoggerFactory.getLogger("SLF4j");
log.info("SLF4j");
}
}

报错:

1
2
3
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

这是因为SLF4J的绑定器没有一个具体的日志处理实现日志功能,如果再引入一个绑定包,如:log4j的绑定 即可。

1
2
3
4
5
6
7
8
9
10
11
        <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
或绑定jul
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.25</version>
</dependency>
slf4j实现原理

Logger log = LoggerFactory.getLogger(SLF4j.class);

主要是通过这句代码去拿具体的实现,获取log对象的源码:

1
2
3
4
5
6
7
8
9
10
11
12
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
if (DETECT_LOGGER_NAME_MISMATCH) {
Class<?> autoComputedCallingClass = Util.getCallingClass();
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
autoComputedCallingClass.getName()));
Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
}
}
return logger;
}

绑定日志框架:

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
    private final static void bind() {
try {
Set<URL> staticLoggerBinderPathSet = null;
// skip check under android, see also
// http://jira.qos.ch/browse/SLF4J-328
if (!isAndroid()) {
//去classpath下找STATIC_LOGGER_BINDER_PATH,STATIC_LOGGER_BINDER_PATH值为"org/slf4j/impl/StaticLoggerBinder.class",
//即所有slf4j的实现,\提供的jar包路径下,一定是有"org/slf4j/impl/StaticLoggerBinder.class"存在
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// the next line does the binding 选取一个StaticLoggerBinder.class来创建一个单例
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError ncde) {
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
} else {
failedBinding(ncde);
throw ncde;
}
} catch (java.lang.NoSuchMethodError nsme) {
String msg = nsme.getMessage();
if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
INITIALIZATION_STATE = FAILED_INITIALIZATION;
Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
Util.report("Your binding is version 1.5.5 or earlier.");
Util.report("Upgrade your binding to version 1.6.x.");
}
throw nsme;
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}

//获取所有加载的实现放到set中 避免在系统中同时引入多个slf4j的实现,所以接收的地方是一个Set。
static Set<URL> findPossibleStaticLoggerBinderPathSet() {
// 通过ClassLoader加载"org/slf4j/impl/StaticLoggerBinder.class"
Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>();
try {
ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
Enumeration<URL> paths;
if (loggerFactoryClassLoader == null) {
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
}
while (paths.hasMoreElements()) {
URL path = paths.nextElement();
staticLoggerBinderPathSet.add(path);
}
} catch (IOException ioe) {
Util.report("Error getting resources from path", ioe);
}
return staticLoggerBinderPathSet;
}

/**
* 若在class path中找到多个绑定类,则打印警告信息
*/
private static void reportMultipleBindingAmbiguity(Set<URL> binderPathSet) {
if (isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
Util.report("Class path contains multiple SLF4J bindings.");
for (URL path : binderPathSet) {
Util.report("Found binding in [" + path + "]");
}
Util.report("See " + MULTIPLE_BINDINGS_URL + " for an explanation.");
}
}

编译期间,编译器会选择其中一个StaticLoggerBinder.class进行绑定,这个地方sfl4j也在reportActualBinding方法中报告了绑定的是哪个日志框架:

1
2
3
4
5
6
private static void reportActualBinding(Set<URL> binderPathSet) {
// binderPathSet can be null under Android
if (binderPathSet != null && isAmbiguousStaticLoggerBinderPathSet(binderPathSet)) {
Util.report("Actual binding is of type [" + StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr() + "]");
}
}

最后得到日志对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    public static Logger getLogger(String name) {
//不同的StaticLoggerBinder其getLoggerFactory实现不同 返回一个ILoggerFactory实例 StaticLoggerBinder.getSingleton().getLoggerFactory();
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}

public Logger getLogger(String name) {
Logger slf4jLogger = (Logger)this.loggerMap.get(name);
if (slf4jLogger != null) {
return slf4jLogger;
} else {
org.apache.log4j.Logger log4jLogger;
////调用日志框架实现生成"org.apache.log4j.Logger "
if (name.equalsIgnoreCase("ROOT")) {
log4jLogger = LogManager.getRootLogger();
} else {
log4jLogger = LogManager.getLogger(name);
}
//使用适配器包装"org.apache.log4j.Logger "
Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
Logger oldInstance = (Logger)this.loggerMap.putIfAbsent(name, newInstance);
return (Logger)(oldInstance == null ? newInstance : oldInstance);
}
}

桥接器会调用的日志框架实现的相关代码生成其内部的Logger(此Logger与org.slf4j.Logger)不兼容,再通过适配器包装日志框架实现内部的Logger。

日志系统桥接器

我们在项目中一般不直接使用日志实现框架,而是使用外观模式:日志门面组件+桥接器+日志实现框架,这样即使项目更换日志种类,只需更换桥接器和日志实现框架,也就是只更换Jar包就可以了,代码无需做任何改动。

1
2
3
4
5
6
7
8
9
日志系统桥接器说白了就是一种偷天换日的解决方案。

比如log4j-over-slf4j,即log4j -> slf4j的桥接器,这个库定义了与log4j一致的接口(包名、类名、方法签名均一致),但是接口的实现却是对slf4j日志接口的包装,即间接调用了slf4j日志接口,实现了对日志的转发。

但是,jul-to-slf4j是个意外例外,毕竟JDK自带的logging包排除不掉啊,其实是利用jdk-logging的Handler机制,在root logger上install一个handler,将所有日志劫持到slf4j上。要使得jul-to-slf4j生效,需要执行:
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();

spring boot中的日志初始化模块已经包括了该逻辑,故无需手动调用。在使用其他框架时,建议在入口类处的static{ }区执行,确保尽早初始化。

想想一下:

如果log4j -> slf4j,slf4j -> log4j两个桥接器同时存在会出现什么情况?互相委托,无限循环,堆栈溢出。

slf4j -> logback,slf4j -> log4j两个桥接器同时存在会如何?

两个桥接器都会被slf4j发现,在slf4j中定义了优先顺序,优先使用logback,仅会报警,发现多个日志框架绑定实现;

但有一些框架中封装了自己的日志facade,如果其对绑定日志实现定义的优先级顺序与slf4j不一致,优先使用log4j,那整个程序中就有两套日志系统在工作。

log4j2桥接器由log4j2提供,其他桥接器由slf4j提供。

官方桥接案例说明:

20200420223904

详细说明这三个案例

左上图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
现状:目前的应用程序中已经使用了如下混杂方式的API来进行日志的编程:

commons-logging
log4j1
jdk-logging
现在想统一将日志的输出交给logback

解决办法:

第一步:将上述日志系统全部无缝先切换到slf4j

去掉commons-logging(其实去不去都可以),使用jcl-over-slf4j将commons-logging的底层日志输出切换到slf4j
去掉log4j1(必须去掉),使用log4j-over-slf4j,将log4j1的日志输出切换到slf4j
使用jul-to-slf4j,将jul的日志输出切换到slf4j

第二步:使slf4j选择logback来作为底层日志输出加入以下jar包:
slf4j-api
logback-core
logback-classic
下面的2张图和上面就很类似

右上图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
现状:目前的应用程序中已经使用了如下混杂方式的API来进行日志的编程:

commons-logging
jdk-logging
现在想统一将日志的输出交给log4j1

解决办法:
第一步:将上述日志系统全部无缝先切换到slf4j

去掉commons-logging(其实去不去都可以),使用jcl-over-slf4j将commons-logging的底层日志输出切换到slf4j
使用jul-to-slf4j,将jul的日志输出切换到slf4j

第二步:使slf4j选择log4j1来作为底层日志输出 加入以下jar包:
slf4j-api
log4j
slf4j-log4j12(集成包)

左下图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
现状:目前的应用程序中已经使用了如下混杂方式的API来进行日志的编程:
commons-logging
log4j

现在想统一将日志的输出交给jdk-logging

解决办法:

第一步:将上述日志系统全部无缝先切换到slf4j
去掉commons-logging(其实去不去都可以),使用jcl-over-slf4j将commons-logging的底层日志输出切换到slf4j
去掉log4j1(必须去掉),使用log4j-over-slf4j,将log4j1的日志输出切换到slf4j

第二步:使slf4j选择jdk-logging来作为底层日志输出

加入以下jar包:

slf4j-api
slf4j-jdk14(集成包)
logback
simple-log

各种jar包总结

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

log4j:log4j1的全部内容

log4j2:

log4j-api:log4j2定义的API
log4j-core:log4j2上述API的实现

logback:

logback-core:logback的核心包
logback-classic:logback实现了slf4j的API

commons-logging:

commons-logging:commons-logging的原生全部内容
log4j-jcl:commons-logging到log4j2的桥梁
jcl-over-slf4j:commons-logging到slf4j的桥梁
slf4j绑定实际的日志框架
1
2
3
4
5
6
7
使用slf4j的API进行编程,底层想使用log4j1来进行实际的日志输出

slf4j-jdk14:slf4j到jdk-logging的桥梁
slf4j-log4j12:slf4j到log4j1的桥梁
log4j-slf4j-impl:slf4j到log4j2的桥梁
logback-classic:slf4j到logback的桥梁
slf4j-jcl:slf4j到commons-logging的桥梁
日志框架转向slf4j
1
2
3
4
5
使用log4j1的API进行编程,但是想最终通过logback来进行输出,所以就需要先将log4j1的日志输出转交给slf4j来输出,slf4j再交给logback来输出。日志框架之间的切换

jul-to-slf4j:jdk-logging到slf4j的桥梁
log4j-over-slf4j:log4j1到slf4j的桥梁
jcl-over-slf4j:commons-logging到slf4j的桥梁
冲突说明
jcl-over-slf4j 与 slf4j-jcl 冲突
1
2
jcl-over-slf4j: commons-logging切换到slf4j
slf4j-jcl : slf4j切换到commons-logging

如果这两者共存的话,必然造成相互委托,造成内存溢出

log4j-over-slf4j 与 slf4j-log4j12 冲突
1
2
log4j-over-slf4j : log4j1切换到slf4j
slf4j-log4j12 : slf4j切换到log4j1

如果这两者共存的话,必然造成相互委托,造成内存溢出。但是log4j-over-slf4内部做了一个判断,可以防止造成内存溢出:

即判断slf4j-log4j12 jar包中的org.slf4j.impl.Log4jLoggerFactory是否存在,如果存在则表示冲突了,抛出异常提示用户要去掉对应的jar包,代码如下,在slf4j-log4j12 jar包的org.apache.log4j.Log4jLoggerFactory中:

1
2
3
4
5
6
7
8
9
10
11
12
static {
try {
Class.forName("org.apache.log4j.Log4jLoggerFactory");
String part1 = "Detected both log4j-over-slf4j.jar AND bound slf4j-log4j12.jar on the class path, preempting StackOverflowError. ";
String part2 = "See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.";
Util.report(part1);
Util.report(part2);
throw new IllegalStateException(part1 + part2);
} catch (ClassNotFoundException var2) {
//TODO:增加日志
}
}
jul-to-slf4j 与 slf4j-jdk14 冲突
1
2
jul-to-slf4j : jdk-logging切换到slf4j
slf4j-jdk14 : slf4j切换到jdk-logging

如果这两者共存的话,必然造成相互委托,造成内存溢出

Spring源码分析一IOC源码

发表于 2019-01-21 | 分类于 spring

结构

Spring Bean的创建是典型的工厂模式,这一系列的Bean工厂,也即IOC容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在Spring中有许多的IOC容器的实现供用户选择和使用,其相互关系如下:
20200507220411
AnnotationConfigApplicationContext 是基于注解来使用的,它不需要配置文件,采用 java 配置类和各种注解来配置,是比较简单的方式,也是趋势,我倾向使用这种方式。

BeanFactory 简介

20200507223153
BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范,BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。最终的默认实现类是 DefaultListableBeanFactory。

为何要定义这么多层次的接口:他使用的场合,它主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。例如 ListableBeanFactory 接口表示这些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定义 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
public interface BeanFactory {    

//对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象, 如果需要得到工厂本身,需要转义。
String FACTORY_BEAN_PREFIX = "&";

//根据bean的名字,获取在IOC容器中得到bean实例
Object getBean(String name) throws BeansException;

//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
Object getBean(String name, Class requiredType) throws BeansException;

//提供对bean的检索,看看是否在IOC容器有这个名字的bean
boolean containsBean(String name);

//根据bean名字得到bean实例,并同时判断这个bean是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

//得到bean实例的Class类型
Class getType(String name) throws NoSuchBeanDefinitionException;

//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);

}

IOC构造初始化

Spring的Ioc的初始化过程,实际上就是把beanName和BeanDefinition注册到DefaultListableBeanFactory的map中。

使用注解的方式创建代码

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);

Spring中出来注解Bean定义的类有两个: AnnotationConfigApplicationContext和AnnotationConfigWebApplicationContext。

AnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的web版本,两者的用法以及对注解的处理方式几乎没有什么差别

AnnotationConfigApplicationContext构造方法如下:

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
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

/**
* 这个类顾名思义是一个reader,一个读取器
* 读取什么呢?还是顾名思义AnnotatedBeanDefinition意思是读取一个被加了注解的bean,此属性在构造方法中被实例化。
*/
private final AnnotatedBeanDefinitionReader reader;

/**
* 这是一个扫描器,扫描所有加了注解的bean,此属性在构造方法中被实例化。
*/
private final ClassPathBeanDefinitionScanner scanner;
/**
* 初始化一个bean的读取和扫描器
*
* 默认构造函数,如果直接调用这个默认构造方法,需要在稍后通过调用其register() 去注册配置类(javaconfig),并调用refresh()方法刷新容器,
* 触发容器对注解Bean的载入、解析和注册过程
*/
public AnnotationConfigApplicationContext() {
/**
* 会先执行父类的构造方法
* public GenericApplicationContext() {
* this.beanFactory = new DefaultListableBeanFactory();
* }
* 创建一个读取注解的Bean定义读取器 BeanDefinition
*/
this.reader = new AnnotatedBeanDefinitionReader(this);
//可以用来扫描包或者类,继而转换成bd,但是实际上我们扫描包工作不是scanner这个对象来完成的,是spring 内部自己new的一个ClassPathBeanDefinitionScanner
//这里的scanner仅仅是为了程序员能够在外部调用AnnotationConfigApplicationContext对象的scan方法
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
super(beanFactory);
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

/**
* 这个构造方法需要传入一个被javaconfig注解了的配置类,然后会把这个被注解了javaconfig的类通过注解读取器读取后继而解析
*/
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
//这里由于他有父类,故而会先调用父类的构造方法,然后才会调用自己的构造方法
//在自己构造方法中初始一个读取器和扫描器
this();
register(annotatedClasses);
refresh();
}

public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
//设置环境
@Override
public void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment);
this.reader.setEnvironment(environment);
this.scanner.setEnvironment(environment);
}

/**
* 注册单个bean给容器
* 比如有新加的类可以用这个方法,但是注册注册之后需要手动调用refresh方法去触发容器解析注解
*
* 他可以注册一个配置类、还可以单独注册一个bean
*/
public void register(Class<?>... annotatedClasses) {
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
this.reader.register(annotatedClasses);
}
// 调用的是bean扫描器ClassPathBeanDefinitionScanner的scan方法
public void scan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
this.scanner.scan(basePackages);
}
}

主要属性:

AnnotatedBeanDefinitionReader——BeanDefinition解析器用来解析带注解的bean
ClassPathBeanDefinitionScanner——bean的扫描器 用来扫描类
注册解析传入的配置类(使用类配置的方式进行解析)
调用容器的refresh方法初始化容器

1、注册BeanDefinition的注册器

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 这里的BeanDefinitionRegistry registry是通过在AnnotationConfigApplicationContext 的构造方法中传进来的this
* 由此说明AnnotationConfigApplicationContext是一个BeanDefinitionRegistry类型的类
*
* GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry
*
* 看到他实现了BeanDefinitionRegistry证明上面的说法,
* BeanDefinitionRegistry 顾名思义就是BeanDefinition的注册器
* @param registry
*/
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}

2、注册扫描器

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 ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {

Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
//为容器设置加载Bean定义的注册器
this.registry = registry;
//是否使用默认过滤规则
if (useDefaultFilters) {
registerDefaultFilters();
}
//设置环境
setEnvironment(environment);
//为容器设置资源加载器
setResourceLoader(resourceLoader);
}

//向容器注册过滤规则
@SuppressWarnings("unchecked")
protected void registerDefaultFilters() {
//向要包含的过滤规则中添加@Component注解类
//@Service和@Controller都是Component,因为这些注解都添加了@Component注解
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
//获取当前类的类加载器
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
//向要包含的过滤规则添加JavaEE6的@ManagedBean注解
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
//向要包含的过滤规则添加@Named注解
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}

扫描注册

1
2
3
4
5
6
7
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { 
//这里由于他有父类,故而会先调用父类的构造方法,然后才会调用自己的构造方法
//在自己构造方法中初始一个读取器和扫描器
this();
register(annotatedClasses);
refresh();
}

this()方法,会去初始化AnnotatedBeanDefinitionReader读取器和ClassPathBeanDefinitionScanner扫描器

如果传入类,最终会执行到:

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
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
/**
* 根据指定的bean创建一个AnnotatedGenericBeanDefinition
* 这个AnnotatedGenericBeanDefinition可以理解为一个数据结,包含了类的其他信息,比如一些元信息scope,lazy等等
*/
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
/**
* 判断这个类是否需要跳过解析
* 通过代码可以知道spring判断是否跳过解析,主要判断类有没有加注解
*/
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
//?
abd.setInstanceSupplier(instanceSupplier);
/**
* 得到类的作用域,默认singleton
*/
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
/**
* 把类的作用域添加到数据结构结构中
*/
abd.setScope(scopeMetadata.getScopeName());
/**
* 生成类的名字通过beanNameGenerator,可以自己扩展
*/
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
/**
* 处理类当中的通用注解:Lazy DependsOn Primary Role等等注解
* 处理完成之后processCommonDefinitionAnnotations中依然是把他添加到数据结构当中
*
*/
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

/**
* 如果在向容器注册注解Bean定义时,使用了额外的限定符注解则解析
* 关于Qualifier和Primary前面的课当中讲过,主要涉及到spring的自动装配
*
* 这里需要注意的
* byName和qualifiers这个变量是Annotation类型的数组,里面存不仅仅是Qualifier注解
* 理论上里面里面存的是一切注解,所以可以看到下面的代码spring去循环了这个数组
* 然后依次判断了注解当中是否包含了Primary,是否包含了Lazyd
*/
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
// 如果配置了@Primary注解,如果加了则作为首选
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
//懒加载,前面加过
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
//如果使用了除@Primary和@Lazy以外的其他注解,则为该Bean添加一个根据名字自动装配的限定符
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
customizer.customize(abd);
}

/**
* 这个BeanDefinitionHolder也是一个数据结构
*/
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);

/**
* ScopedProxyMode 这个知识点比较复杂,需要结合web去理解
*/
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

/**
* 把上述的这个数据结构注册给registry registy就是AnnotatonConfigApplicationContext
* AnnotatonConfigApplicationContext在初始化的時候通过调用父类构造方法实例化了一个DefaultListableBeanFactory
*
* registerBeanDefinition里面就是把definitionHolder这个数据结构包含的信息注册到 DefaultListableBeanFactory这个工厂
*/
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

如果是个包名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int scan(String... basePackages) {
// 获取当前注册bean的数量
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

doScan(basePackages);

// 注册配置处理器
if (this.includeAnnotationConfig) {
//往BeanDefinitionMap注册
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
// 返回此次注册的数量
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

具体扫描操作

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
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
// 遍历需要扫描的包路径
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 扫描basePackage路径下的java文件, 符合条件的并把它转成BeanDefinition类型
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

for (BeanDefinition candidate : candidates) {
// 解析scope属性 并绑定
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
// 查看是否配置类是否指定bean的名称,如没指定则使用类名首字母小写
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
// 这两个if处理lazy、Autowire、DependencyOn、initMethod、enforceInitMethod、destroyMethod、enforceDestroyMethod、Primary、Role、Description这些逻辑的
if (candidate instanceof AbstractBeanDefinition) {
//如果这个类是AbstractBeanDefinition的子类,则为他设置默认值,比如lazy,init destory
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
// 检查并且处理常用的注解 把常用注解的值设置到AnnotatedBeanDefinition当中
// 当前前提是这个类必须是AnnotatedBeanDefinition类型的,说白了就是加了注解的类
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//检查bean是否存在
if (checkCandidate(beanName, candidate)) {
//又包装了一层
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
//检查scope是否创建,如未创建则进行创建
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//加入到map当中 注册BeanDefinition
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}

findCandidateComponents(basePackage)方法,这个方法里就是具体的扫描逻辑。

1
2
3
4
5
6
7
8
9
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
//判断是否使用Filter指定忽略包不扫描
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {//扫描包
return scanCandidateComponents(basePackage);
}
}

这里是扫描逻辑,主要过程:

根据包路径,扫描所有.class文件
根据包路径,生成.class对应的Resource对象
通过ASM获取class元数据,并封装在MetadataReader元数据读取器中
判断该类是否符合过滤规则
判断该类是否为独立的类、具体的类
加入到集合中
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
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//组装扫描路径(组装完成后是这种格式:classpath*:com/../config/**/*.class)
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//asm 读取class文件
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
//根据资源对象通过反射获取资源对象的MetadataReader
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//查看配置类是否有@Conditional一系列的注解,然后是否满足注册Bean的条件
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
else {
if (debugEnabled) {
logger.debug("Ignored because not a concrete top-level class: " + resource);
}
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not matching any filter: " + resource);
}
}
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to read candidate component class: " + resource, ex);
}
}
else {
if (traceEnabled) {
logger.trace("Ignored because not readable: " + resource);
}
}
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}

这里最主要的是isCandidateComponent判断规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//判断元信息读取器读取的类是否符合容器定义的注解过滤规则@CompoentScan的过滤规则支持5种 (注解、类、正则、aop、自定义)
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
//如果读取的类的注解在排除注解过滤规则中,返回false
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
//如果读取的类的注解在包含的注解的过滤规则中,则返回ture
for (TypeFilter tf : this.includeFilters) {
//判断当前类的注解是否match规则
if (tf.match(metadataReader, getMetadataReaderFactory())) {
//是否有@Conditional注解,进行相关处理
return isConditionMatch(metadataReader);
}
}
//如果读取的类的注解既不在排除规则,也不在包含规则中,则返回false
return false;
}

通过 isCandidateComponent(metadataReader),在这个方法中 有 tf.match(metadataReader, getMetadataReaderFactory())

Spring就是这样发现@Configuration、@Controller、@Service这些注解修饰的类的。过滤方法:

1
2
3
4
5
6
//是否是独立的类、具体的类
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

这个方法的作用是,判断该类是否为:顶层的类(没有父类或静态内部类)、具体的类(不是抽象类或接口)
类型封装

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
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}

// 处理Imported 的情况
//就是当前这个注解类有没有被别的类import
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
//如果同一个配置类被处理两次,两次都属于被import的则合并导入类,返回。如果配置类不是被导入的,则移除旧使用新的配置类。
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an imports.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}

// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
//一个map,用来存放扫描出来的bean(注意这里的bean不是对象,仅仅bean的信息,因为还没到实例化这一步)
this.configurationClasses.put(configClass, configClass);
}

处理内部类

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
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {

// Recursively process any member (nested) classes first
//处理内部类
processMemberClasses(configClass, sourceClass);

// 处理@PropertySource注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}

// 处理 @ComponentScan 注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
//扫描普通类:componentScan=com.hu:::扫描出来所有@@Component 并且把扫描的出来的普通bean放到map当中
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
//检查扫描出来的类当中是否还有configuration
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
//检查
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}

/**
* 上面的代码就是扫描普通类----@Component
* 并且放到了map当中
*/
// Process any @Import annotations
//处理Import注解 imports 3种情况
//ImportSelector
//普通类
//ImportBeanDefinitionRegistrar

//递归 这里和内部递归调用时候的情况不同
/**
* 这里处理的import是需要判断我们的类当中时候有@Import注解
* 如果有这把@Import当中的值拿出来,是一个类
* 比如@Import(xxxxx.class),那么这里便把xxxxx传进去进行解析,在解析的过程中如果发觉是一个importSelector那么就回调selector的方法
* 返回一个字符串(类名),通过这个字符串得到一个类,继而在递归调用本方法来处理这个类。
*
* 判断一组类是不是imports(3种import)
*/
processImports(configClass, sourceClass, getImports(sourceClass), true);

// 处理@ImportResource 注解
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}

//处理包含@Bean注解的方法
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

// Process default methods on interfaces
// 处理普通方法
processInterfaces(configClass, sourceClass);

// Process superclass, if any
// 处理子类
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}

// No superclass -> processing is complete
return null;
}

至此包扫描也结束了,已经把扫描到的类存入到了集合中,结下来就是解析注册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
public void refresh() throws BeansException, IllegalStateException {
//为了避免refresh还没结束,再次发起启动或者销毁容器引起的冲突
synchronized (this.startupShutdownMonitor) {
////准备工作包括设置启动时间,是否激活标识位,
// 初始化属性源(property source)配置
prepareRefresh();
//BeanFactory的初始化、Bean的加载和注册等事件
//返回一个factory 为什么需要返回一个工厂
//因为要对工厂进行初始化
//这里同时会限制容器只能调用一个该refresh方法,在obtainFreshBeanFactory()中的refreshBeanFactory()方法中限制了
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
* 准备工厂,设置BeanFactory的类加载器、bean表达式解释器等一些初始化操作,添加几个 BeanPostProcessor、手动注册几个特殊的bean。 *
*
* 这里会把EnvironmentAware,EmbeddedValueResolverAware,ResourceLoaderAware,ApplicationEventPublisherAware,MessageSourceAware,ApplicationContextAware
*
* 这几个接口忽略掉,Spring不会自动注入这些接口的实例,同时注册两个后置处理器:ApplicationContextAwareProcessor、ApplicationListenerDetector,
* 第一个主要是 给实现了上面六个接口的对象注入相应的资源
*/
prepareBeanFactory(beanFactory);

try {
// TODO: Spring的一个扩展点
// 这个方法在这里是一个空方法,但是在后面的Web的上下文是XmlWebApplicationContext有用的
// 如果有Bean实现了BeanFactoryPostProcessor接口,那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。
// 具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做一些操作。
postProcessBeanFactory(beanFactory);
//在spring的环境中去执行已经被注册的 后置工厂处理器
//自定义的BeanFactoryPostProcessor 和spring内部自己定义
invokeBeanFactoryPostProcessors(beanFactory);
//注册beanPostProcessor,注意不是BeanFactoryPostProcessor
//此接口有两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization分别会在Bean初始化之前和初始化之后得到执行
registerBeanPostProcessors(beanFactory);
// 初始化当前 ApplicationContext 的 MessageSource,国际化的相关
initMessageSource();
//初始化应用事件广播器
initApplicationEventMulticaster();
// TODO:扩展点 比如初始化其他特殊的bean
onRefresh();
// 注册事件监听器
registerListeners();
// bean还没有初始化。这个方法就是负责初始化所有的没有设置懒加载的singleton bean
finishBeanFactoryInitialization(beanFactory);
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();
}
}
}

准备工作

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
//做一些准备工作,记录容器的启动时间、标记“已启动”状态、检查环境变量等
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);

if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}

// Initialize any placeholder property sources in the context environment
//初始化加载配置文件方法,并没有具体实现,一个留给用户的扩展点
//例如WebContext初始化参数用的,把xml中的${key}替换成实际值,这个后面用web上下文证实一下
initPropertySources();

// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
//// 检查环境变量
//其中检查环境变量的核心方法为,简单来说就是如果存在环境变量的value 为空的时候就抛异常,然后停止启动Spring
getEnvironment().validateRequiredProperties();

// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}

BeanFactory的初始化、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
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 核心
refreshBeanFactory();
// 返回刚刚创建的 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}

protected final void refreshBeanFactory() throws BeansException {
// 判断当前ApplicationContext是否存在BeanFactory,如果存在的话就销毁所有 Bean,关闭 BeanFactory
// 注意,一个应用可以存在多个BeanFactory,这里判断的是当前ApplicationContext是否存在BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 初始化DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用
customizeBeanFactory(beanFactory);
// 这个方法会根据配置,加载各个 Bean的BeanDefinitions,然后放到 BeanFactory 中
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

准备工厂,设置BeanFactory的类加载器、bean表达式解释器等一些初始化操作,添加几个 BeanPostProcessor、手动注册几个特殊的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
/**
* 设置BeanFactory的类加载器、配置其标准的特征,添加几个 BeanPostProcessor、手动注册几个特殊的bean 、上下文的加载器ClassLoader和post-processors回调
* 此处的beanFactory参数等于DefaultListableFactory
*/
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 设置为加载当前ApplicationContext类的类加载器
beanFactory.setBeanClassLoader(getClassLoader());
//BeanExpressionResolver bean表达式解释器,能够获取bean当中的属性在前台页面
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
//对象与string类型的转换 <property red="dao">
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

// 添加一个后置管理器 ApplicationContextAwareProcessor
// 能够在bean中获得到各种*Aware(*Aware都有其作用):在所有实现了Aware接口的bean在初始化的时候,这个 processor负责回调
// TODO: 这里是Spring的又一个扩展点:这个我们很常用,如我们会为了获取 ApplicationContext 而 实现接口 ApplicationContextAware
// 注意:它不仅仅回调 ApplicationContextAware,还会负责回调 EnvironmentAware、ResourceLoaderAware 等
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
//
//如果某个 bean 依赖于以下几个接口的实现类,spring容器就不会帮你注入这几个接口的实例,在自动装配的时候忽略它们,Spring 会通过其他方式来处理这些依赖。
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
// 特殊的几个 bean 赋值,如果有 bean 依赖了以下几个,会注入这边相应的值
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

//注册一个后置处理器,主要检查是否实现了ApplicationListener接口,如果实现了就加入当前的applicationContext的applicationListeners列表
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

// 如果存在bean名称为loadTimeWeaver的bean则注册一个BeanPostProcessor
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}

// 意思是如果自定义的Bean中没有名为"systemProperties"、"environment"和"systemEnvironment"的Bean,那么 Spring 会 "手动" 注册一个
// Key为"systemProperties"和"systemEnvironment",Value为Map,
// 这两个Bean就是一些系统配置和系统环境信息
// Register default environment beans.
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}

在spring的环境中去执行已经被注册的 后置工厂处理器

1、getBeanFactoryPostProcessors()得到自己定义的(就是 没有交给spring管理,没有加上@Component)

2、得到spring内部自己维护的BeanDefinitionRegistryPostProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// 注意:getBeanFactoryPostProcessors()是获取手动给spring的BeanFactoryPostProcessor
// 所谓的自定义的就是你手动调用AnnotationConfigApplicationContext.addBeanFactoryPostProcesor()添加到list的
// 并不仅仅是程序员写的,可以加@companent也可以不加,如果加了getBeanFactoryPostProcessors()这个地方得不到,是spring自己扫描得到。
// 为什么得不到?getBeanFactoryPostProcessors()这个方法是直接获取一个list,
// 这个list是在AnnotationConfigApplicationContext被定义
/*
自己定义的BeanFactoryProcessor可以有两种方式
1、实现BeanFactoryProcessor接口
2、实现BeanDefinitionRegistryPostProcessor 因为BeanDefinitionRegistryPostProcessor实现了BeanFactoryProcessor于是可以猜想实现bdrp和实现bfp是能够完成不同的功能。
其实也可以理解,因为bdrp是子类,他肯定扩展了bfp的功能
*/
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}

循环所有的BeanDefinitionRegistryPostProcessor,该方法内部postProcessor.postProcessBeanDefinitionRegistry

org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors

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
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
Set<String> processedBeans = new HashSet<>();

if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

//自定义的beanFactoryPostProcessors
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {//BeanDefinitionRegistryPostProcessor BeanfactoryPostProcessor
regularPostProcessors.add(postProcessor);
}
}

//这个currentRegistryProcessors 放的是spring内部自己实现了BeanDefinitionRegistryPostProcessor接口的对象
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
//BeanDefinitionRegistryPostProcessor 等于 BeanFactoryPostProcessor
//getBeanNamesForType 根据bean的类型获取bean的名字ConfigurationClassPostProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
//这个地方可以得到一个BeanFactoryPostProcessor,因为是spring默认在最开始自己注册的
//为什么要在最开始注册这个呢?
//因为spring的工厂需要许解析去扫描等等功能
//而这些功能都是需要在spring工厂初始化完成之前执行
//要么在工厂最开始的时候、要么在工厂初始化之中,反正不能再之后
//因为如果在之后就没有意义,因为那个时候已经需要使用工厂了
//所以这里spring'在一开始就注册了一个BeanFactoryPostProcessor,用来插手springfactory的实例化过程
//在这个地方断点可以知道这个类叫做ConfigurationClassPostProcessor
//ConfigurationClassPostProcessor那么这个类能干嘛呢?可以参考源码
//下面我们对这个牛逼哄哄的类(他能插手spring工厂的实例化过程还不牛逼吗?)重点解释
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
//排序不重要,况且currentRegistryProcessors这里也只有一个数据
sortPostProcessors(currentRegistryProcessors, beanFactory);
//合并list,不重要(为什么要合并,因为还有自己的)
registryProcessors.addAll(currentRegistryProcessors);
//最重要。注意这里是方法调用
//执行所有BeanDefinitionRegistryPostProcessor

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
//执行完成了所有BeanDefinitionRegistryPostProcessor
//这个list只是一个临时变量,故而要清除
currentRegistryProcessors.clear();

// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}

// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
//执行BeanFactoryPostProcessor的回调,前面不是吗?
//前面执行的BeanFactoryPostProcessor的子类BeanDefinitionRegistryPostProcessor的回调
//这是执行的是BeanFactoryPostProcessor postProcessBeanFactory
//ConfuguratuonClassPpostProcssor
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
//自定义BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}

else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}

// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
//ConfigurationClassPostProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}

// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}

调用扩展方法postProcessBeanDefinitionRegistry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);

processConfigBeanDefinitions(registry);
}

拿出的所有bd,然后判断bd时候包含了@Configuration、@Import,@Compent。。。注解

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
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//定义一个list存放app 提供的bd(项目当中提供了@Compent)
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
//获取容器中注册的所有bd名字
//7个
String[] candidateNames = registry.getBeanDefinitionNames();

/**
* Full
* Lite
*/
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
//果BeanDefinition中的configurationClass属性为full或者lite,则意味着已经处理过了,直接跳过
//这里需要结合下面的代码才能理解
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//判断是否是Configuration类,如果加了Configuration下面的这几个注解就不再判断了
// add(Component.class.getName());
// candidateIndicators.add(ComponentScan.class.getName());
// candidateIndicators.add(Import.class.getName());
// candidateIndicators.add(ImportResource.class.getName());
// beanDef == appconfig
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
//BeanDefinitionHolder 也可以看成一个数据结构
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}

// Return immediately if no @Configuration classes were found
//如果没有配置类就直接返回
if (configCandidates.isEmpty()) {
return;
}

// 排序,根据order,不重要
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});

// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;
// 如果BeanDefinitionRegistry是SingletonBeanRegistry子类的话, 由于我们当前传入的是DefaultListableBeanFactory,是SingletonBeanRegistry 的子类
// 因此会将registry强转为SingletonBeanRegistry
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) {
//是否有自定义的
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
//SingletonBeanRegistry中有id为 org.springframework.context.annotation.internalConfigurationBeanNameGenerator
//如果有则利用他的,否则则是spring默认的
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}

if (this.environment == null) {
this.environment = new StandardEnvironment();
}

// Parse each @Configuration class
//实例化ConfigurationClassParser 为了解析各个配置类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);

//实例化2个set,candidates用于将之前加入的configCandidates进行去重
//因为可能有多个配置类重复了
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
//alreadyParsed用于判断是否处理过
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
//map.keyset
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);

// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}

/**
* 这里值得注意的是扫描出来的bean当中可能包含了特殊类 比如ImportBeanDefinitionRegistrar那么也在这个方法里面处理
* 但是并不是包含在configClasses当中 configClasses当中主要包含的是importSelector
* 因为ImportBeanDefinitionRegistrar在扫描出来的时候已经被添加到一个list当中去了
*/

//bd 到 map 除却普通
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);

candidates.clear();
//由于我们这里进行了扫描,把扫描出来的BeanDefinition注册给了factory
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());

// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}

if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
1、的到bd当中描述的类的元数据(类的信息)
2、判断是不是加了@Configuration   metadata.isAnnotated(Configuration.class.getName())
3、如果加了@Configuration,添加到一个set当中,把这个set传给下面的方法去解析
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
public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}

AnnotationMetadata metadata;
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
//如果BeanDefinition 是 AnnotatedBeanDefinition的实例,并且className 和 BeanDefinition中 的元数据 的类名相同
// 则直接从BeanDefinition 获得Metadata
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
}
else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
//如果BeanDefinition 是 AbstractBeanDefinition的实例,并且beanDef 有 beanClass 属性存在
//则实例化StandardAnnotationMetadata
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
metadata = new StandardAnnotationMetadata(beanClass, true);
}
else {
try {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not find class file for introspecting configuration annotations: " + className, ex);
}
return false;
}
}

//判断当前这个bd中存在的类是不是加了@Configruation注解
//如果存在则spring认为他是一个全注解的类
if (isFullConfigurationCandidate(metadata)) {
//如果存在Configuration 注解,则为BeanDefinition 设置configurationClass属性为full
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
//判断是否加了以下注解,摘录isLiteConfigurationCandidate的源码
// candidateIndicators.add(Component.class.getName());
// candidateIndicators.add(ComponentScan.class.getName());
// candidateIndicators.add(Import.class.getName());
// candidateIndicators.add(ImportResource.class.getName());
//如果不存在Configuration注解,spring则认为是一个部分注解类
else if (isLiteConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}

// It's a full or lite configuration candidate... Let's determine the order value, if any.
Integer order = getOrder(metadata);
if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
}

return true;
}

org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)

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
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
//根据BeanDefinition 的类型 做不同的处理,一般都会调用ConfigurationClassParser#parse 进行解析
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
//
try {
if (bd instanceof AnnotatedBeanDefinition) {
//解析注解对象,并且把解析出来的bd放到map,但是这里的bd指的是普通的
//何谓不普通的呢?比如@Bean 和各种beanFactoryPostProcessor得到的bean不在这里put
//但是是这里解析,只是不put而已
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}

//处理延迟加载的importSelect?为什么要延迟加载,估计就是为了延迟吧
processDeferredImportSelectors();
}

注册beanPostProcessor(registerBeanPostProcessors(beanFactory);)

注意不是BeanFactoryPostProcessor,此接口有两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization分别会在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
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

//从beanDefinitionMap中得到所有的BeanPostProcessor
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

// Separate between BeanPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// priorityOrderedPostProcessors.remove(1);
// First, register the BeanPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

// Next, register the BeanPostProcessors that implement Ordered.
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);

// Now, register all regular BeanPostProcessors.
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

// Finally, re-register all internal BeanPostProcessors.
// internalPostProcessors.remove(1);
sortPostProcessors(internalPostProcessors, beanFactory);

registerBeanPostProcessors(beanFactory, internalPostProcessors);

// Re-register post-processor for detecting inner beans as ApplicationListeners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

初始化当前 ApplicationContext 的 MessageSource

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
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//查找是否包含了名为messageSource的bean,如果没有,创建一个默认的
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// Make MessageSource aware of parent MessageSource.
//判断是否有父类且是一个分层级的messageSource,如果是将父容器的的messageSource设置到里边
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
// Only set parent context as parent MessageSource if no parent MessageSource
// registered already.
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using MessageSource [" + this.messageSource + "]");
}
}
else {
// Use empty MessageSource to be able to accept getMessage calls.
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
"': using default [" + this.messageSource + "]");
}
}
}

初始化应用事件广播器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 如果用户配置了自定义事件广播器,就使用用户的
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 使用默认的时间广播器
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}

初始化其他

1
2
// TODO:扩展点  比如初始化其他特殊的bean
onRefresh();

完成此上下文的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
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}

// 先初始化 LoadTimeWeaverAware 类型的 Bean
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// 停止使用用于类型匹配的临时类加载器
beanFactory.setTempClassLoader(null);
// 冻结所有的bean定义,即已注册的bean定义将不会被修改或后处理
beanFactory.freezeConfiguration();
//实例化所有的单例对象
beanFactory.preInstantiateSingletons();
}

发布事件

1
2
3
4
5
6
7
8
9
10
11
12
protected void finishRefresh() {
//清除上下文级别的资源缓存(例如来自扫描的ASM元数据)
clearResourceCaches();
// 初始化LifecycleProcessor
initLifecycleProcessor();
// 这个方法的内部实现是启动所有实现了Lifecycle接口的bean
getLifecycleProcessor().onRefresh();
// 发布ContextRefreshedEvent事件
publishEvent(new ContextRefreshedEvent(this));
// 检查spring.liveBeansView.mbeanDomain是否存在,有就会创建一个MBeanServer
LiveBeansView.registerApplicationContext(this);
}

spring初始化bean工厂的过程

Bean的加载

refresh()还调用了很多后处理器的方法,其中有一个方法 finishBeanFactoryInitialization(),意味着非延迟加载的类,将在这一步实例化,完成类的加载。

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

1
2
3
4
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
/**
* 通过 name 获取 beanName。这里不使用 name 直接作为 beanName 有两个原因
* 1、name 可能会以 & 字符开头,表明调用者想获取 FactoryBean 本身,而非 FactoryBean 实现类所创建的 bean。
* 在 BeanFactory 中,FactoryBean 的实现类和其他的 bean 存储方式是一致的,即 <beanName, bean>,beanName 中是没有 & 这个字符的。
* 所以我们需要 将 name 的首字符 & 移除,这样才能从缓存里取到 FactoryBean 实例。
* 2、还是别名的问题,转换需要 &beanName
*/
final String beanName = transformedBeanName(name);
Object bean;

/**
* 这个方法在初始化的时候会调用,在getBean的时候也会调用 为什么需要这么做呢?
* 也就是说spring在初始化的时候先获取这个对象 判断这个对象是否被实例化好了(普通情况下绝对为空====有一种情况可能不为空)
*
* 从spring的bean容器中获取一个bean,由于spring中bean容器是一个map(singletonObjects)
* 所以你可以理解getSingleton(beanName)等于beanMap.get(beanName)
* 由于方法会在spring环境初始化的时候(就是对象被创建的时候调用一次)调用一次 还会在getBean的时候调用一次
*
* 所以再调试的时候需要特别注意,不能直接断点在这里, 需要先进入到annotationConfigApplicationContext.getBean(IndexDao.class)
* 之后再来断点,这样就确保了我们是在获取这个bean的时候调用的
*
* 需要说明的是在初始化时候调用一般都是返回null
*
* lazy
*/
Object sharedInstance = getSingleton(beanName);
//如果已经初始化过了,且没有传args参数就代表是get,直接取出返回
if (sharedInstance != null && args == null) {
//这里的代码是对于日志的记录,方便我们以后阅读应该注释,不影响spring功能
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}

/**
* 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果sharedInstance 是 FactoryBean 类型的,
* 则需调用 getObject 工厂方法获取真正的bean 实例。
*
* 如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已。
*/
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}

else {
/**
* 原型
* 如果是原型不应该在初始化的时候创建
*/
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}

// Check if bean definition exists in this factory.
// 如果当前BeanDefinition不存在这个bean且具有父BeanFactory
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 {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
if (!typeCheckOnly) {
//添加到alreadyCreated set集合当中,表示他已经创建过
markBeanAsCreated(beanName);
}
// 创建bean
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);

// Guarantee initialization of beans that the current bean depends on.
// 先初始化依赖的所有 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);
}
}
}
// 如果是单例的
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
// create bean
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);
}
// 如果是prototype
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
// 执行创建 Bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
// 如果不是 singleton 和 prototype 那么就是自定义的scope、例如Web项目中的session等类型,这里就交给自定义scope的应用方去实现
else {
String scopeName = mbd.getScope();
final 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);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}

// Check if required type matches the type of the actual bean instance.
//检查bean的类型
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}

#doGetBean(…) 方法的代码量比较多,可以看出 bean 的加载过程是一个非常复杂的过程,会涉及到各种各样的情况处理。

可以分为以下几个过程:

  • 转换 beanName,方法传入的 name 并不一定就是 beanName,可以传入 aliasName,FactoryBean,所以这里需要进行简单的转换过程。
  • 尝试从缓存中加载单例 bean。
  • bean的实例化。
  • 原型模式的依赖检查。Spring只会解决单例模式的循环依赖,对于原型模式的循环依赖都是直接抛出 BeanCurrentlyInCreationException 异常。
  • 尝试从 parentBeanFactory 获取 bean 实例。如果 parentBeanFactory != null && !containsBeanDefinition(beanName) 则尝试从 parentBeanFactory 中获取 bean 实例对象,因为 !containsBeanDefinition(beanName) 就意味着定义的 xml 文件中没有 beanName 相应的配置,这个时候就只能从 parentBeanFactory 中获取。
  • 获取 RootBeanDefinition,并对其进行合并检查。从缓存中获取已经解析的 RootBeanDefinition 。同时,如果父类不为 null 的话,则会合并父类的属性。
  • 依赖检查。某个 bean 依赖其他 bean ,则需要先加载依赖的 bean。
  • 对不同的 scope 进行处理。
  • 类型转换处理。如果传递的 requiredType 不为 null,则需要检测所得到 bean 的类型是否与该 requiredType 一致。如果不一致则尝试转换,当然也要能够转换成功,否则抛出 BeanNotOfRequiredTypeException 异常。

获取原始beanName

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
protected String transformedBeanName(String name) {
return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
BeanFactoryUtils.transformedBeanName(name)方法主要是去除 FactoryBean 的修饰符

//对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = "&";

public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
String beanName = name;
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
return beanName;
}

//循环处理,从aliasMap中根据aliasName获取真实beanName,直到获取到的真实beanName为null
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}

从缓存中获取单例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
//单例bean的缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

//单例对象工厂缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

//预加载单例bean缓存
//存放的 bean 不一定是完整的
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

//对于单例模式的Bean整个IOC容器中只创建一次,不需要重复创建
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//从单例缓存中获取单例bean
Object singletonObject = this.singletonObjects.get(beanName);
//如果缓存中没有 并且 该bean正在创建
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
// earlySingletonObjects 中没有,且允许提前创建
if (singletonObject == null && allowEarlyReference) {
//从缓存中获取 ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//从单例工厂中获取bean
singletonObject = singletonFactory.getObject();
//存入early
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}

该方法就是从 singletonObjects、earlySingletonObjects、 singletonFactories 三个缓存中获取,这里也是 Spring 解决 bean 循环依赖的关键之处。

第一步,从singletonObjects中获取Bean对象

第二步,如果获取不到且Bean正在创建中,从earlySingletonObjects获取Bean对象

第三步,如果获取不到且允许提前创建,从singletonFactories获取FactoryBean

第四步,如果不为null,则通过FactoryBean.getObject()获取Bean,然后将其加入到 earlySingletonObjects ,并且从 singletonFactories 删除,两者是互斥的,主要用来解决循环依赖的问题

创建 bean 实例对象

如果缓存中没有,也没有 parentBeanFactory ,则会调用 #createBean(String beanName, RootBeanDefinition mbd, Object[] args) 方法,创建 bean 实例。

1
2
3
// AbstractBeanFactory.java
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException;

抽象方法的默认实现是在类 AbstractAutowireCapableBeanFactory 中实现,该方法其实只是做一些检查和验证工作,真正的初始化工作是由 #doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) 方法来实现。

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
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
//如果是.factoryBean则从缓存删除
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
/**
* 创建 bean 实例,并将实例包裹在 BeanWrapper 实现类对象中返回。
* createBeanInstance中包含三种创建 bean 实例的方式:
* 1. 通过工厂方法创建 bean 实例
* 2. 通过构造方法自动注入(autowire by constructor)的方式创建 bean 实例
* 3. 通过无参构造方法方法创建 bean 实例
*
* 若 bean 的配置信息中配置了 lookup-method 和 replace-method,则会使用 CGLIB
* 增强 bean 实例。关于lookup-method和replace-method
*/
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}

// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
// 循环调用实现了MergedBeanDefinitionPostProcessor接口的postProcessMergedBeanDefinition方法
// Spring对这个接口有几个默认的实现,其中大家最熟悉的一个是操作@Autowired注解的
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}

// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// 解决循环依赖问题
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//当正在创建A时,A依赖B,此时通过(B将A作为ObjectFactory放入单例工厂中进行early expose,此处B需要引用A,但A正在创建,从单例工厂拿到ObjectFactory,从而允许循环依赖)
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

// Initialize the bean instance.
Object exposedObject = bean;
try {
//设置属性,非常重要
populateBean(beanName, mbd, instanceWrapper);
// 这里是处理bean初始化完成后的各种回调,例如init-method、InitializingBean 接口、BeanPostProcessor 接口
// aop就是在这里完成的处理
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}

// 同样的,如果存在循环依赖
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}

// Register bean as disposable.
// 把bean注册到相应的Scope中
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}

return exposedObject;
}

实例化 bean

如果缓存中没有 BeanWrapper 实例对象或者该 bean 不是 singleton,则调用 createBeanInstance方法创建 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
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.
// 确保已经加载了此 class
Class<?> beanClass = resolveBeanClass(mbd, beanName);

// 检测一个类的访问权限spring默认情况下对于非public的类是允许访问的。
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}

Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}

/**
* 采用工厂方法实例化
* 如果工厂方法不为空,则通过工厂方法构建 bean 对象
*/
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}

// Shortcut when re-creating the same bean...
/**
* 从spring的原始注释可以知道这个是一个Shortcut,什么意思呢?
* 当多次构建同一个 bean 时,可以使用这个Shortcut, 也就是说不在需要次推断应该使用哪种方式构造bean
* 比如在多次构建同一个prototype类型的 bean 时,就可以走此处的shortcut
* 这里的 resolved 和 mbd.constructorArgumentsResolved 将会在 bean 第一次实例化的过程中被设置
*/
// 是否第一次
boolean resolved = false;
// 是否采用构造函数注入
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
//如果已经解析了构造方法的参数,则必须要通过一个带参构造方法来实例
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
// 通过构造方法自动装配的方式构造 bean 对象
return autowireConstructor(beanName, mbd, null, null);
}
else {
//通过默认的无参构造方法进行
return instantiateBean(beanName, mbd);
}
}

// Candidate constructors for autowiring?
//由后置处理器决定返回哪些构造方法
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}

// No special handling: simply use no-arg constructor.
//使用默认的无参构造方法进行初始化
return instantiateBean(beanName, mbd);
}

属性填充

属性填充其实就是将 BeanDefinition 的属性值赋值给 BeanWrapper 实例对象的过程。

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
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
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;
// 如果返回 false,代表不需要进行后续的属性设值,也不需要再经过其他的 BeanPostProcessor 的处理
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}

if (!continueWithPropertyPopulation) {
return;
}

// bean的所有属性
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
// 通过名字找到所有属性值,如果是 bean 依赖,先初始化依赖的 bean。记录依赖关系
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
// 通过类型装配。复杂一些
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}

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

if (hasInstAwareBpps || needsDepCheck) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// 这里就是上方曾经提到过得对@Autowired处理的一个BeanPostProcessor了
// 它会对所有标记@Autowired、@Value 注解的属性进行设值
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}

if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}

初始化 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
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//执行后置处理的befor
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

try {
//执行bean的声明周期回调中的init方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
//执行后置处理器的after方法
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

return wrappedBean;
}

从 bean 实例中获取对象

无论是从单例缓存中获取的 bean 实例 还是通过 createBean() 方法来创建的 bean 实例,最终调用getObjectForBeanInstance()方法,来获取最终的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
//获取给定Bean的实例对象,主要是完成FactoryBean的相关处理
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

//容器已经得到了Bean实例对象,这个实例对象可能是一个普通的Bean,
//也可能是一个工厂Bean,如果是一个工厂Bean,则使用它创建一个Bean实例对象,
//如果调用本身就想获得一个容器的引用,则指定返回这个工厂Bean实例对象

//若为工厂类引用(name 以 & 开头) 且 Bean实例也不是 FactoryBean
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}

// Now we have the bean instance, which may be a normal bean or a FactoryBean.

//如果类型不是FactoryBean,直接返回
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}

Object object = null;
//若 BeanDefinition 为 null,则从缓存中加载 Bean 对象
if (mbd == null) {
//从Bean工厂缓存中获取给定名称的Bean实例对象
object = getCachedObjectForFactoryBean(beanName);
}

// 若 object 依然为空,则可以确认,beanInstance 一定是 FactoryBean 。从而,使用 FactoryBean 获得 Bean 对象
if (object == null) {
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// 检测是否定义 beanName
if (mbd == null && containsBeanDefinition(beanName)) {
//从容器中获取指定名称的Bean定义,如果继承基类,则合并基类相关属性
mbd = getMergedLocalBeanDefinition(beanName);
}
//如果从容器得到Bean定义信息,并且Bean定义信息不是虚构的,
//则让工厂Bean生产Bean实例对象
boolean synthetic = (mbd != null && mbd.isSynthetic());
//调用FactoryBeanRegistrySupport类的getObjectFromFactoryBean方法,
//实现工厂Bean生产Bean对象实例的过程
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}

当该实例对象为非 FactoryBean 类型,直接返回给定的 Bean 实例对象 beanInstance 。当该实例对象为FactoryBean 类型,从 FactoryBean ( beanInstance ) 中,获取 Bean 实例对象。

getObjectFromFactoryBean

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
//Bean工厂生产Bean实例对象
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
//为单例模式且缓存中存在
if (factory.isSingleton() && containsSingleton(beanName)) {
//多线程同步,以防止数据不一致
synchronized (getSingletonMutex()) {
//从缓存中获取指定的 factoryBean
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 为空,则从 FactoryBean 中获取对象
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
object = alreadyThere;
}
else {
if (shouldPostProcess) {
try {
// 对从 FactoryBean 获取的对象进行后处理 生成的对象将暴露给 bean 引用
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's singleton object failed", ex);
}
}
//将生产的实例对象添加到Bean工厂缓存中
this.factoryBeanObjectCache.put(beanName, object);
}
}
return object;
}
}
// 为空,则从 FactoryBean 中获取对象
else {
Object object = doGetObjectFromFactoryBean(factory, beanName);
// 需要后续处理
if (shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
}
return object;
}
}

Bean生命周期

20200516174812

spring源码日志技术分析

发表于 2019-01-20 | 分类于 spring

spring5.和spring4.日志技术实现

新建一个项目,引入spring5的pom依赖。

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>

可以看到spring5默认使用了jcl包,测试代码:

1
2
3
4
public static void main(String[] args){
AnnotationConfigApplicationContext cx = new AnnotationConfigApplicationContext(App.class);
cx.start();
}

运行后得到日志信息:

1
2
三月 04, 2019 8:55:15 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@bebdb06: startup date [Wed Mar 04 20:55:15 CST 2020]; root of context hierarchy

引入log4j的包,再运行,日志发现没任何变化。

如果把spring5改为spring4,进行相同的测试,运行后的日志有变化。

通过查看文档得知:spring5使用的spring的jcl(spring改了jcl的代码)来记录日志的,但是jcl不能直接记录日志,采用switch循环优先的原则。

20200420224148

spring4当中依赖的是原生的jcl(原生的jcl就是如果有log4j就会优先使用)。

20200420224203

spring jcl分析:

1
2
3
public AbstractApplicationContext() {
//实例化
this.logger = LogFactory.getLog(this.getClass());

获取分析:

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
//定义
private static enum LogApi {
LOG4J,
SLF4J_LAL,
SLF4J,
JUL;

private LogApi() {
}
}

private static LogFactory.LogApi logApi;

//初始化值
static {
//默认使用jul
logApi = LogFactory.LogApi.JUL;
ClassLoader cl = LogFactory.class.getClassLoader();

//看能否加载对应的类
try {
//如果能加载log2j2
cl.loadClass("org.apache.logging.log4j.spi.ExtendedLogger");
logApi = LogFactory.LogApi.LOG4J;
} catch (ClassNotFoundException var6) {
try {
cl.loadClass("org.slf4j.spi.LocationAwareLogger");
logApi = LogFactory.LogApi.SLF4J_LAL;
} catch (ClassNotFoundException var5) {
try {
cl.loadClass("org.slf4j.Logger");
logApi = LogFactory.LogApi.SLF4J;
} catch (ClassNotFoundException var4) {
}
}
}

}

//获取日志对象
public static Log getLog(Class<?> clazz) {
return getLog(clazz.getName());
}

public static Log getLog(String name) {
switch(logApi) {
case LOG4J://使用的log4j2而不是log4j
return LogFactory.Log4jDelegate.createLog(name);
case SLF4J_LAL:
return LogFactory.Slf4jDelegate.createLocationAwareLog(name);
case SLF4J:
return LogFactory.Slf4jDelegate.createLog(name);
default:
//默认使用jul
return LogFactory.JavaUtilDelegate.createLog(name);
}
}

此时,如果引入log4j2的包,日志会走log4j2。

总结:

1
2
spring5使用的spring的jcl(spring改了jcl的代码)来记录日志的,但是jcl不能直接记录日志,采用switch循环优先的原则。
spring4当中依赖的是原生的jcl(原生的jcl就是如果有log4j就会优先使用)。

spring源码构建spring源码环境

发表于 2019-01-20 | 分类于 spring
下载安装gradle

gradle-src为gralde源码,gradle-bin为安装包。

解压gradle-bin到目录。

配置

配置环境变量到path,配置仓库:gradle默认的本地仓库,新增GRADLE_USER_HOME,路径配置成需要的本地仓库路径即可。

github官网下载spring5.0x源码

构建:

导入到idea,选择gradle路径,配置对应的目录。

打开import-into-idea.md文件,文件中介绍导入到idea的介绍。

注(翻译):

1
2
3
4
5
6
 a,因为其他项目需要依赖spring-core和spring-oxm,所以我们导入后需要先编译这两个包

b,spring-aspects需要依赖三方jar(精通aop的应该知道spring-aop和aspectj的关系,在使用注解方式实现spring-aop的时候,需要导入aspectj的jar,有兴趣的可以去查阅下官方文档aop相关文档),这里编译可能会报错,不影响我们编译使用,我们先不处理

c,使用工具编译时,可能会出现内存溢出情况,这里我们编译时需要增加相关参数
-XX:MaxPermSize=2048m -Xmx2048m -XX:MaxHeapSize=2048m
导入构建

步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1、spring源码包中 build.gradle    

repositories属性添加 阿里云的仓库
maven { url "http://maven.aliyun.com/nexus/content/groups/public" }
mavenCentral()

开始的 buildscript {
repositories {
中添加:
maven { url "https://maven.aliyun.com/repository/spring-plugin" }
maven { url "https://maven.aliyun.com/nexus/content/repositories/spring-plugin" }

2、在idea中打开项目,设置好gradle路径。构建完成(耗时)

先编译 spring-core和spring-oxm

出现的问题:

1、构建错误:No such property: immutableValues for class: org.gradle.api.internal.tasks.DefaultTaskDependency

1
2
把spring-beans中文件spring-beans.gradle的注释:
//compileGroovy.dependsOn = compileGroovy.taskDependencies.values - "compileJava"

2、Could not find method useJUnitPlatform()

1
2
3
4
5
注释:spring-test.gradle中这行代码
// useJUnitPlatform {
// includeEngines 'junit-jupiter'
// excludeTags 'failing-test-case'
// }

3、spring core核心代码报错

1
2
3
4
cmd进入该目录 
.....spring-framework-5.0.x\spring-core
执行:
gradle build会生成两个核心jar包

4、kotlin编译导致的问题

1
2
3
4
5
Circular dependency between the following tasks:
:spring-beans:compileGroovy
\--- :spring-beans:compileJava
\--- :spring-beans:compileKotlin
\--- :spring-beans:compileGroovy (*)

编译github的最新spring源码发现该问题,主要是kotlin版本不一致导致的

20200420223223

两处修改为一致

20200420223237

5、Calls to static methods in Java interfaces are prohibited in JVM target 1.6. R

20200420223653

上一页1…101112…25下一页
初晨

初晨

永远不要说你知道本质,更别说真相了。

249 日志
46 分类
109 标签
近期文章
  • WebSocket、Socket、TCP、HTTP区别
  • Springboot项目的接口防刷
  • 深入理解Volatile关键字及其实现原理
  • 使用vscode搭建个人笔记环境
  • HBase介绍安装与操作
© 2018 — 2020 Copyright
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4