前言
写作背景:最近(2022-08-17)在准备面试换工作(base上海),不喜欢背八股文,网上关于Spring循环依赖的文章浅尝辄止,也罢,还是自己边看源码边写来梳理一下这个问题。
先给结论:Spring通过三个Map解决set方法注入的循环依赖问题:
- 第一层
Map<String,Object> singletonObjects
:暴露已经完全创建好的单例bean; - 第二层
Map<String,Object> earlySingletonObjects
:暴露早期Bean,可以理解为刚new的实例,还未执行初始化; - 第三层
Map<String, ObjectFactory> singletonFactories
:暴露创建早期Bean的工厂;
本文涉及前置知识储备包括:
- SpringIOC容器生命周期;
- SpringBean生命周期;
- SpringAOP创建代理对象;
本文通过结合源码手写代码的方式,一次搞定Spring循环依赖的原理。其优点在于:
- 将复杂类继承结构平铺,比普通源码分析容易理解;
- 忽略与循环依赖无关代码,集中攻克循环依赖原理;
- 从2个Map->3个Map->SpringAop层层递进,更深一步理解循环依赖;
注:手写代码忽略与循环依赖无关的所有细节,包括但不限于:BeanDefinition合并、FactoryBean、bean构造方法选择、线程安全、异常处理。但是能保证所有提到的Bean生命周期顺序是正确的。
一、2个Map
Q:2个Map能解决循环依赖问题吗?
A:能。但有缺陷。
核心模型
1)BeanDefinition
BeanDefinition:构建Bean需要的元数据信息。
一般来说,Spring全家桶中BeanDefinition一般有三种来源:
- 基于xml配置的IOC容器:例如ClassPathXmlApplicationContext通过XmlBeanDefinitionReader加载xml文件中的bean定义;
- 基于注解配置的IOC容器:ConfigurationClassPostProcessor(BeanDefinitionRegistryPostProcessor)解析Configuration注解类的BeanDefinition,注册更多BeanDefinition(如@Bean、@Import等);
- 通过api直接注册BeanDefinition:比如springboot中通过AnnotatedBeanDefinitionReader.register方法,将启动类注册为BeanDefinition;
这里我们简化BeanDefinition模型,只包含下面两个属性:
- clazz:Bean的Class类型;
- dependencies:Bean依赖的beanName集合,依赖一般不由用户直接提供,放在这里主要是为了简化后续的代码逻辑。(DefaultListableBeanFactory#doResolveDependency)
public class BeanDefinition {
/**
* BeanClass
*/
private Class<?> clazz;
/**
* dependency beanName
*/
private Collection<String> dependencies = new HashSet<>();
}
复制代码
2)InitializingBean
这里InitializingBean和Spring的完全一致,这里提到这个接口的目的是:展现第一层Map和第二层Map中的Bean的区别。
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
复制代码
BeanFactory
基于上述两个模型再加上JDK的Map,就可以实现一个简单的BeanFactory了,这里只使用前两层Map:
- singletonObjects:存放完全构建完成的Bean,即首次顺利走完getBean方法的单例bean;
- earlySingletonObjects:存放刚new出来的Bean,只做了部分依赖注入(populateBean),没初始化(initializeBean);
public class SimpleBeanFactory {
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new HashMap<>();
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>();
}
复制代码
提供用户registerBeanDefinition方法,注册BeanDefinition。
// SimpleBeanFactory.java
// beanName -> BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName, beanDefinition);
}
复制代码
提供用户getBean核心方法,根据beanName查找单例Bean。
public Object getBean(String name) throws Exception {
// 1. get from 2 levels cache
Object o = getSingleton(name);
if (o != null) {
return o;
}
// 2. new
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
if (beanDefinition == null) {
return null;
}
Object bean = beanDefinition.getClazz().newInstance();
// 3. add level 2 cache(beanName -> earlySingletonObject)
earlySingletonObjects.put(name, bean);
// 4. populateBean
for (String dependenceBeanName : beanDefinition.getDependencies()) {
autowireByName(dependenceBeanName, bean);
}
// 5. initializeBean
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
// 6. add level 1 cache, delete level 2 cache
addSingleton(name, bean);
return bean;
}
复制代码
二层Map解决循环依赖的思路很朴素,就是在所有对象被new出来之后,立刻放进earlySingletonObjects中缓存。
这样如果A依赖B,在B依赖A的时候能从earlySingletonObjects获取A的早期对象,只是A尚未完全初始化。如A和B循环依赖,整个调用栈大致如下:
getBean(A) {
getSingleton(A) // find from n level cache miss
new(A) // new
cache.put(A) // cache early bean A
populateBean(A) { // inject
getBean(B) {
getSingleton(B) // find from n level cache miss
new(B) // new
cache.put(B) // cache early bean B
populateBean(B) { // inject
getBean(A) {
getSingleton(A) // hit early bean A
}
}
initializeBean(A) // initialize
// cache B level2 to level1
}
}
initializeBean(A)
// cache A level2 to level1
}
复制代码
接下来看一下每个小方法的实现。
1)getSingleton
这个方法在Spring中的原型是DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean),其中第一个参数是BeanName,第二个参数和三级Map有关,暂时忽略。
简化后的getSingleton方法先从一级Map取Bean,如果取不到则从二级Map取Bean。只有发生循环依赖的时候这里能从earlySingletonObjects获取到依赖bean。
private Object getSingleton(String name) {
// 1. 已经完全构建完成的单例bean
Object o = singletonObjects.get(name);
if (o != null) {
return o;
}
// 2. 刚new的bean
// 可能执行了部分populateBean 还未执行initializeBean
return earlySingletonObjects.get(name);
}
复制代码
2)new
这个方法在Spring中的原型是AbstractAutowireCapableBeanFactory#createBeanInstance。
在Spring中对构造方法的选择有一套复杂的逻辑,简化后new这一步简单采用反射调用无参构造实现。
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
if (beanDefinition == null) {
return null;
}
Object bean = beanDefinition.getClazz().newInstance();
复制代码
3)缓存早期Bean
在Spring中,是没有单纯往earlySingletonObjects缓存bean的方法的。这里是为了展现2个map实现循环依赖处理。
earlySingletonObjects.put(name, bean);
复制代码
4)populateBean
在Spring中,这一步主要是做依赖注入,包括Autowired和Value注解(AutowiredAnnotationBeanPostProcessor)、Resource注解(CommonAnnotationBeanPostProcessor)都是在这一步注入Bean的,原型是AbstractAutowireCapableBeanFactory#populateBean。
这里我们简化采用autowire by name的方式,通过field做依赖注入。
第一个入参是依赖的BeanName,第二个入参是当前正在创建的bean,等待依赖注入。
这里递归调用了getBean方法,查找依赖bean(Spring原型DependencyDescriptor#resolveCandidate)。
private void autowireByName(String dependenceBeanName, Object newInstance) throws Exception {
Object dependenceBean = getBean(dependenceBeanName);
Class<?> newInstanceClass = newInstance.getClass();
// inject by field name(AUTOWIRE_BY_NAME)
Field field = newInstanceClass.getDeclaredField(dependenceBeanName);
field.setAccessible(true);
field.set(newInstance, dependenceBean);
field.setAccessible(false);
}
复制代码
5)initializeBean
initializeBean在bean依赖注入完成后,执行初始化步骤。
在Spring中的原型是AbstractAutowireCapableBeanFactory#initializeBean包括:
- 部分Aware方法回调;
- BeanPostProcessor前置处理(@PostConstruct);
- InitializingBean回调;
- 自定义initMethod;
- BeanPostProcessor后置处理(Aop? );
这里我们简化实现,只是回调InitializingBean。之所以这里把InitializingBean回调放入循环依赖的原理解析,是因为earlySingletonObjects中的bean都是没有经过初始化阶段的。
if (bean instanceof InitializingBean) {
((InitializingBean) bean).afterPropertiesSet();
}
复制代码
6)缓存单例bean
在bean完全构造好后,将二层map中的bean移动到一层map中,作为ioc容器后续运行过程中的长期缓存使用。Spring原型是DefaultSingletonBeanRegistry#addSingleton,我们这里简化实现只考虑2层map。
private void addSingleton(String name, Object newInstance) {
singletonObjects.put(name, newInstance);
earlySingletonObjects.remove(name);
}
复制代码
测试
简单写两个循环依赖的模型,通过上面实现的SimpleBeanFactory测试2个map是否能解决循环依赖问题。
ClassRoom:教室类依赖学生类Student,实现InitializingBean接口,通过init标志位标识是否执行初始化完成。
public class ClassRoom implements InitializingBean {
private boolean init;
private Student student;
public Student getStudent() {
return student;
}
public boolean isInit() {
return init;
}
@Override
public void afterPropertiesSet() throws Exception {
this.init = true;
System.out.println(this.getClass().getSimpleName() + " init, student.init = " + student.isInit());
}
}
复制代码
Student:学生类依赖教室类,同样实现InitializingBean接口,通过init标志位标识是否执行初始化完成。
public class Student implements InitializingBean {
private boolean init;
private ClassRoom classRoom;
public ClassRoom getClassRoom() {
return classRoom;
}
public boolean isInit() {
return init;
}
@Override
public void afterPropertiesSet() throws Exception {
this.init = true;
System.out.println(this.getClass().getSimpleName() + " init, classRoom.init = " + classRoom.isInit());
}
}
复制代码
测试类:构造ClassRoom和Student的BeanDefinition,形成循环依赖,通过getBean方法构造ClassRoom。(平常都是使用ApplicationContext,ApplicationContext会在refresh阶段创建所有单例非延迟加载bean,底层就是循环调用getBean方法)
public class SimpleBeanFactoryTest {
public static void main(String[] args) throws Exception {
SimpleBeanFactory beanFactory = new SimpleBeanFactory();
BeanDefinition student = new BeanDefinition();
student.setClazz(Student.class);
student.addDependency("classRoom");
beanFactory.registerBeanDefinition("student", student);
BeanDefinition classRoom = new BeanDefinition();
classRoom.setClazz(ClassRoom.class);
classRoom.addDependency("student");
beanFactory.registerBeanDefinition("classRoom", classRoom);
ClassRoom classRoomInstance = (ClassRoom) beanFactory.getBean("classRoom");
System.out.println(beanFactory);
System.out.println("ioc容器内的ClassRoom = " + classRoomInstance);
System.out.println("ioc容器内的ClassRoom.student = " + classRoomInstance.getStudent());
System.out.println("ioc容器内的classRoom.student.classRoom = " + classRoomInstance.getStudent().getClassRoom());
}
}
复制代码
控制台输出:
Student init, classRoom.init = false
ClassRoom init, student.init = true
{"singletonObjects":{student=Student@60e53b93, classRoom=ClassRoom@5e2de80c},"earlySingletonObjects":{},"beanDefinitionMap":{student=BeanDefinition@1d44bcfa, classRoom=BeanDefinition@266474c2}}
ioc容器内的ClassRoom = ClassRoom@5e2de80c
ioc容器内的ClassRoom.student = Student@60e53b93
ioc容器内的classRoom.student.classRoom = ClassRoom@5e2de80c
复制代码
运行起来没有问题,可以看到由于ClassRoom->Student->ClassRoom的循环依赖,在Student执行初始化时,由于拿到的ClassRoom仅仅是暴露的早期对象,还未执行初始化方法。
缺陷
既然2个Map可以实现一个支持循环依赖的ioc容器,为什么Spring要使用3个Map?
考虑如果最终ioc容器内ClassRoom是一个代理对象,而不是一个刚new出来的bean。
那么Student在注入ClassRoom早期对象的时候,如果只有2个Map,只能注入一个未被代理的ClassRoom,这与期望可能相违背。
比如ClassRoom里面有个事务操作,如果Student调用ClassRoom的事务方法,结果没有被事务切面拦截,那么就与期望不符。
二、3个Map
根据上面2个Map的结论,引入3个Map主要是为了解决Aop相关的功能,在发生循环依赖时,能正确的将依赖的早期bean被代理,注入目标bean,这使得整个ioc容器内的bean没有二义性。
核心模型
1)BeanPostProcessor
熟悉SpringAop的同学都知道,一般bean都在BeanPostProcessor.postProcessAfterInitialization阶段创建代理对象。
不仅如此,如果要解决循环依赖注入代理对象,还需要SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference提供辅助,协助暴露早期bean构造工厂。
这里我们简化实现,去除BeanPostProcessor中的无关方法,合并SmartInstantiationAwareBeanPostProcessor中的getEarlyBeanReference方法,统一使用BeanPostProcessor接口。
public interface BeanPostProcessor {
/**
* Bean后置处理
* org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization
*/
default Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
/**
* 获取早期bean实例
* org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference
* 如果这里有循环依赖,可以在初始化前(postProcessAfterInitialization)提前创建代理对象
*/
default Object getEarlyBeanReference(Object bean, String beanName) {
return bean;
}
}
复制代码
2)ObjectFactory
和Spring中的定义一致,一个函数式接口,返回一个object。
仅考虑循环依赖,一般返回的就是早期bean。
@FunctionalInterface
public interface ObjectFactory<T> {
/**
* Return an instance (possibly shared or independent)
* of the object managed by this factory.
* @return the resulting instance
*/
T getObject();
}
复制代码
BeanFactory
在2个Map的基础上,我们这里再加入一层map和BeanPostProcessor。
- singletonFactories:用于构造早期bean的工厂方法;
- beanPostProcessors:bean后置处理器;
public class BeanFactory {
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new HashMap<>();
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>();
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();
}
复制代码
暴露registerBeanPostProcessor方法,用于注册BeanPostProcessor。
public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
beanPostProcessors.add(beanPostProcessor);
}
复制代码
提供用户getBean核心方法,根据beanName查找单例Bean。相较于SimpleBeanFactory,共涉及以下几个改动点:
- getSingleton:支持3级map;
- 不直接暴露早期bean,而是暴露早期bean的工厂ObjectFactory;
- initializeBean:在初始化阶段的最后,调用BeanPostProcessor.postProcessAfterInitialization,通常在这里将原始bean包装为代理bean;
- getSingletonFromLevel1Level2:如果发生循环依赖,在外层bean的创建过程中,没有做任何代理逻辑,那么取ObjectFactory产生的bean,可能是个早期暴露的代理bean(因为是由于内层依赖bean通过获取早期bean创建的);
- addSingleton:支持3级map;
public Object getBean(String name) throws Exception {
// 1. get from 3 levels cache
// it may delete level 3 cache -> level 2 cache
Object o = getSingleton(name);
if (o != null) {
return o;
}
// 标记bean已经被创建(伏笔)
alreadyCreated.add(name);
// 2. new
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
if (beanDefinition == null) {
return null;
}
Object bean = beanDefinition.getClazz().newInstance();
final Object finalBean = bean;
// 3. add level 3 cache(beanName -> ObjectFactory)
singletonFactories.put(name, () -> {
Object result = finalBean;
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
// 如果BeanPostProcessor要创建代理,且在循环依赖时也要注入代理对象(而非原始对象),那么要实现getEarlyBeanReference
result = beanPostProcessor.getEarlyBeanReference(result, name); // create proxy
}
return result;
});
Object exposedObject = bean;
// 4. populateBean
for (String dependenceBeanName : beanDefinition.getDependencies()) {
autowireByName(dependenceBeanName, bean);
}
// 5. initializeBean
if (exposedObject instanceof InitializingBean) {
((InitializingBean) exposedObject).afterPropertiesSet();
}
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) { // create proxy
exposedObject = beanPostProcessor.postProcessAfterInitialization(exposedObject, name);
}
// 6. get from level 2 cache
Object earlySingletonReference = getSingletonFromLevel1Level2(name);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
// 如果在当前bean的生命周期中没有被包装 exposedObject==bean
// 采用二级缓存中的bean,即ObjectFactory创建的,可能是个代理bean
exposedObject = earlySingletonReference;
}
}
// 7. add level 1 cache, delete level 2,3 cache
addSingleton(name, exposedObject);
return exposedObject;
}
复制代码
接下来一个一个看改动点。
1)getSingleton
与SimpleBeanFactory相比,在earlySingletonObjects二级map获取bean为空后,支持从三级ObjectFactory构建bean。
构建完成后,清除三级ObjectFactory,将构建完成的早期bean加入earlySingletonObjects二级map。
private Object getSingleton(String name) {
// 1. 已经完全构建完成的单例bean
Object o = singletonObjects.get(name);
if (o != null) {
return o;
}
// 2. 从ObjectFactory生成的早期bean,还未初始化
// 此时的bean在new的基础上,可能被SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference改为代理对象
o = earlySingletonObjects.get(name);
if (o != null) {
return o;
}
// 3. 仅仅暴露为ObjectFactory
// 此时的bean仅仅是个刚new的对象,还未完全初始化(可能执行了部分populateBean,完全没执行initializeBean)
ObjectFactory<?> factory = singletonFactories.get(name);
if (factory == null) {
return null;
}
o = factory.getObject(); // 此处会执行SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference改为代理对象
earlySingletonObjects.put(name, o);
this.singletonFactories.remove(name);
return o;
}
复制代码
2)缓存早期bean构建工厂ObjectFactory
为了解决循环依赖注入代理bean,ObjectFactory在原始bean的基础上,调用BeanPostProcessor.getEarlyBeanReference获取早期代理bean(如果BeanPostProcessor确实是为了创建代理bean的话)。
这样在ClassRoom->Student->ClassRoom(proxy)循环依赖场景下,Student就可以注入ClassRoom的代理对象了(结合2个Map中的伪代码思考,ObjectFactory只不过是创建early bean的一个包装方法)。
// 3. add level 3 cache(beanName -> ObjectFactory)
singletonFactories.put(name, () -> {
Object result = finalBean;
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
// 如果BeanPostProcessor要创建代理,且在循环依赖时也要注入代理对象(而非原始对象),那么要实现getEarlyBeanReference
result = beanPostProcessor.getEarlyBeanReference(result, name); // create proxy
}
return result;
});
复制代码
3)initializeBean
在bean初始化阶段,回调了BeanPostProcessor.postProcessAfterInitialization方法。如果没有发生循环依赖的情况下,一个BeanA要被代理,一般都在这里返回代理对象。
if (exposedObject instanceof InitializingBean) {
((InitializingBean) exposedObject).afterPropertiesSet();
}
for (BeanPostProcessor beanPostProcessor : beanPostProcessors) { // create proxy
exposedObject = beanPostProcessor.postProcessAfterInitialization(exposedObject, name);
}
复制代码
4)getSingletonFromLevel1Level2
准确来说,在讨论循环依赖时,这里只会从第二层map中获取早期bean实例。
这里主要是参考Spring中的原型:DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean),其中第二个boolean传false,代表只查找1级和2级map。
// Spring
// DefaultSingletonBeanRegistry.getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
// 这之后的不差找
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
复制代码
我们简化以后的实现如下:
private Object getSingletonFromLevel1Level2(String name) {
// 1. 已经完全构建完成的单例bean
Object o = singletonObjects.get(name);
if (o != null) {
return o;
}
// 2. 从ObjectFactory生成的bean
// 此时的bean在new的基础上,可能被SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference改为代理对象
return earlySingletonObjects.get(name);
}
复制代码
这里要理解一下这段判断逻辑:
- bean:刚new出来的bean;
- exposedObject:由当前getBean(beanName)方法执行完beanPostProcessor.postProcessAfterInitialization得到的对象
- 如果bean==exposedObject,代表bean在本次getBean过程中并没有被包装
- 但是earlySingletonReference非空,代表在2级map中存在一个这个bean的早期被ObjectFactory暴露的bean,这意味着一定发生了循环依赖,导致当前bean被早期暴露被其他bean注入
- 那么要取被其他bean注入的那个早期bean作为返回,暴露给ioc容器的使用者。因为这个早期bean可能是个通过getEarlyBeanReference在早期创建的代理对象,虽然bean的初始化操作都在目标bean上,但是返回给使用者还是应该返回代理对象
Object earlySingletonReference = getSingletonFromLevel1Level2(name);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
// 如果在当前bean的生命周期中没有被包装 exposedObject==bean
// 采用二级缓存中的bean,即ObjectFactory创建的,可能是个代理bean
exposedObject = earlySingletonReference;
}
}
复制代码
5)缓存单例bean
在一个单例bean创建完成后,从早期bean和早期bean工厂中移除对应entry。
private void addSingleton(String name, Object newInstance) {
singletonObjects.put(name, newInstance);
earlySingletonObjects.remove(name);
singletonFactories.remove(name);
}
复制代码
测试
ClassRoom代理:为了演示代理bean循环依赖注入问题,提供一个教室代理类,重写getStudent并打印日志。
public class ClassRoomProxy extends ClassRoom {
private final ClassRoom classRoom;
public ClassRoomProxy(ClassRoom classRoom) {
this.classRoom = classRoom;
}
@Override
public Student getStudent() {
System.out.println("class room get student from proxy");
return classRoom.getStudent();
}
}
复制代码
测试类:和2个Map的测试类差不多,只是注册了一个BeanPostProcessor,如果bean原来是ClassRoom.class则返回ClassRoomProxy代理bean作为ioc容器内的bean。
public class BeanFactoryTest {
public static void main(String[] args) throws Exception {
BeanFactory beanFactory = new BeanFactory();
BeanDefinition student = new BeanDefinition();
student.setClazz(Student.class);
student.addDependency("classRoom");
beanFactory.registerBeanDefinition("student", student);
BeanDefinition classRoom = new BeanDefinition();
classRoom.setClazz(ClassRoom.class);
classRoom.addDependency("student");
beanFactory.registerBeanDefinition("classRoom", classRoom);
beanFactory.registerBeanPostProcessor(new BeanPostProcessor() {
// 避免重复代理
private final Map<Object, Object> earlyProxyReferences = new HashMap<>();
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (earlyProxyReferences.remove(beanName) != bean) {
return wrapIfNecessary(bean, beanName);
}
return bean;
}
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
earlyProxyReferences.put(beanName, bean);
return wrapIfNecessary(bean, beanName);
}
private Object wrapIfNecessary(Object bean, String beanName) {
if (bean.getClass() == ClassRoom.class) {
return new ClassRoomProxy((ClassRoom) bean);
}
return bean;
}
});
ClassRoom classRoomInstance = (ClassRoom) beanFactory.getBean("classRoom");
System.out.println(beanFactory);
System.out.println("ioc容器内的ClassRoom = " + classRoomInstance);
System.out.println("ioc容器内的classRoom.student.classRoom = " + classRoomInstance.getStudent().getClassRoom());
}
}
复制代码
控制台输出:
Student init, classRoom.init = false
ClassRoom init, student.init = true
{"singletonObjects":{student=Student@5f184fc6, classRoom=bean.ClassRoomProxy@3feba861},"singletonFactories":{},"earlySingletonObjects":{},"beanDefinitionMap":{student=BeanDefinition@5b480cf9, classRoom=BeanDefinition@6f496d9f},"beanPostProcessors":[BeanFactoryTest$1@723279cf],"alreadyCreated":[student, classRoom],"dependentBeanMap":{student=[classRoom], classRoom=[student]}}
ioc容器内的ClassRoom = ClassRoomProxy@3feba861
class room get student from proxy
ioc容器内的classRoom.student.classRoom = ClassRoomProxy@3feba861
复制代码
符合预期,ioc容器内的代理对象=Student注入的ClassRoom代理对象=早期暴露的ClassRoomProxy。
那么如果getEarlyBeanReference使用默认实现,返回原始bean呢?会怎么样?
控制台输出:
Student init, classRoom.init = false
ClassRoom init, student.init = true
{"singletonObjects":{student=Student@5f184fc6, classRoom=bean.ClassRoomProxy@3feba861},"singletonFactories":{},"earlySingletonObjects":{},"beanDefinitionMap":{student=BeanDefinition@5b480cf9, classRoom=BeanDefinition@6f496d9f},"beanPostProcessors":[BeanFactoryTest$1@723279cf],"alreadyCreated":[student, classRoom],"dependentBeanMap":{student=[classRoom], classRoom=[student]}}
ioc容器内的ClassRoom = ClassRoomProxy@3feba861
class room get student from proxy
ioc容器内的classRoom.student.classRoom = ClassRoom@10f87f48
复制代码
Student注入的ClassRoom是个不受ioc容器管理的原始bean,而非代理的ClassRoomProxy。
三、SpringAop与循环依赖
由于上面的3个map的实现是我自己写的,和Spring还是有区别的,最主要的区别就是BeanPostProcessor。现在回到Spring源码,看一下BeanPostProcessor的层级结构。
BeanPostProcessor的层级结构
顶层BeanPostProcessor,在populateBean阶段执行:
- postProcessBeforeInitialization:在执行init方法前被调用,如果返回null,阻断后续其他BeanPostProcessor处理postProcessBeforeInitialization,如果返回非原始bean,那就后续对非原始bean执行初始化;
- postProcessAfterInitialization:在执行init方法后被调用,如果返回null,阻断后续其他BeanPostProcessor处理postProcessAfterInitialization,如果返回非原始bean,那后续流程就使用被包装的bean;
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
复制代码
InstantiationAwareBeanPostProcessor:
- postProcessBeforeInstantiation:在bean被new之前调用,如果返回非空,直接跳过后续所有步骤,如果返回空,就当做什么事都没发生;
- postProcessAfterInstantiation:在populateBean阶段一开始被调用,如果某个postProcessAfterInstantiation方法返回false,将阻断后续populateBean流程,即不做依赖注入;
- postProcessProperties:bean属性后置处理,在populateBean阶段执行,Autowired、Value、Resource注解修饰的bean都是在这里执行注入的,可以返回非空PropertyValues,继续用于后续依赖注入;
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}
@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
return null;
}
}
复制代码
SmartInstantiationAwareBeanPostProcessor:
getEarlyBeanReference:与我们自己实现的BeanPostProcessor的同名方法具有相同的作用,支持在循环依赖发生时,返回早期bean,最常用的情况是返回代理对象。
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor {
default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
return bean;
}
}
复制代码
自动创建代理的两个分支
实战中,可能有人会遇到一个问题,就是两个被Async注解修饰的方法所在类,相互依赖时,在ioc容器启动阶段会启动失败。
比如在开启@EnableAsync的情况下,A、B两个类循环依赖。
@Component
public class A {
@Autowired
private B b;
@Async
public void t() {
}
}
@Component
public class B {
@Autowired
private A a;
@Async
public void t() {
}
}
复制代码
启动会报错:
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Bean with name 'a' has been injected into other beans [b] 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 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
同样是创建代理,为什么@Async启动会失败,而@Transactional却不会呢?
AbstractAdvisorAutoProxyCreator
AbstractAdvisorAutoProxyCreator是自动创建代理辅助抽象类,实现了SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference方法,可以在循环依赖发生时,提前暴露早期bean。
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
复制代码
在spring中最核心的实现类是InfrastructureAdvisorAutoProxyCreator。
他的作用是:
- 筛选BeanDefinition是Advisor类型的;
- 筛选BeanDefinition.role=ROLE_INFRASTRUCTURE(框架内置的BeanDefinition);
- 对于当前正在创建的bean,从上面两步过滤得到的advisor集合中,筛选出需要添加在当前bean上的advisor,创建Aop代理对象;
public class InfrastructureAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator {
@Nullable
private ConfigurableListableBeanFactory beanFactory;
@Override
protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.initBeanFactory(beanFactory);
this.beanFactory = beanFactory;
}
@Override
protected boolean isEligibleAdvisorBean(String beanName) {
return (this.beanFactory != null && this.beanFactory.containsBeanDefinition(beanName) &&
this.beanFactory.getBeanDefinition(beanName).getRole() == BeanDefinition.ROLE_INFRASTRUCTURE);
}
}
复制代码
对于Transactional事务来说,比如在springboot自动配置中,就被@Rule注解声明为框架内置Bean。
所以这类代理对象,都是通过InfrastructureAdvisorAutoProxyCreator这个BeanPostProcessor创建的,且能支持暴露早期代理对象。
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) // 注意这里
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}
}
复制代码
AbstractAdvisingBeanPostProcessor
AbstractAdvisingBeanPostProcessor是另一个自动创建代理的辅助类,只需要用户注入一个Advisor就可以实现代理逻辑,但是它仅仅实现了BeanPostProcessor接口,所以并不具备在循环依赖场景下注入代理对象。
public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {
@Nullable
protected Advisor advisor;
}
复制代码
而Async注解代理对象创建,就是继承AbstractAdvisingBeanPostProcessor的实现AsyncAnnotationBeanPostProcessor。(@Validated注解也是)
那么为什么这种情况下,Spring会报错呢?
原因和刚才我们三个Map实现的BeanFactory最后的演示结果一样,就是ioc容器内的ClassRoom是个代理对象ClassRoomProxy,但是给Student注入的却是一个没被代理的ClassRoom。
如何实现注入原始bean的校验
继续完善我们三个Map的BeanFactory支持与Spring一样的检查工作,
如果当前beanName早期暴露注入其他bean的实例与实际注入容器的bean不一致,就报错。
1)BeanDefinition
在注册BeanDefinition的时候,不光记录BeanName->BeanDefinition的映射关系,还需要反向解析这个bean依赖的bean。
比如A依赖B、B依赖A,那么dependentBeanMap的key就是B,value就是(A)。
如果当B创建完成后,就可以检查A是不是利用了他的早期bean,如果是进一步校验早期beanA和当前即将进入ioc容器的beanA是否一致。
// beanName -> BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
// bean 和 依赖它的bean集合
private final Map<String, Set<String>> dependentBeanMap = new HashMap<>();
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName, beanDefinition);
for (String dependency : beanDefinition.getDependencies()) {
dependentBeanMap.computeIfAbsent(dependency, s -> {
Set<String> set = new HashSet<>();
set.add(beanName);
return set;
});
}
}
复制代码
2)getSingletonFromLevel1Level2
在加入最终一级Map前,增加一个校验逻辑。
如果当前beanName暴露了早期bean,即earlySingletonReference != null;
且当前beanName在创建过程中包装了bean,即exposedObject != bean;
且确实有bean依赖当前bean已经被创建,代表早期bean已经被注入其他bean;
则抛出异常。
// 6. raw inject
Object earlySingletonReference = getSingletonFromLevel1Level2(name);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
// 如果在当前bean的生命周期中没有被包装
// 采用二级缓存中的bean,即代理bean
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && dependentBeanMap.containsKey(name)) {
// 如果在当前bean的生命周期中被包装
// 且有依赖当前bean的其他bean已经提前创建,注入了错误的earlySingletonReference
// 抛出异常
Set<String> dependentBean = dependentBeanMap.getOrDefault(name, Collections.emptySet());
for (String db : dependentBean) {
if (alreadyCreated.contains(db)) {
throw new RuntimeException( "Bean with name '" + name + "' has been injected into other beans " + db +
" 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 " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
复制代码
测试
修改测试类,BeanPostProcessor在getEarlyBeanReference阶段返回原始bean,这样会导致Student注入刚new出来的ClassRoom,而非ClassRoomProxy。
public class BeanFactoryTest {
public static void main(String[] args) throws Exception {
BeanFactory beanFactory = new BeanFactory();
BeanDefinition student = new BeanDefinition();
student.setClazz(Student.class);
student.addDependency("classRoom");
beanFactory.registerBeanDefinition("student", student);
BeanDefinition classRoom = new BeanDefinition();
classRoom.setClazz(ClassRoom.class);
classRoom.addDependency("student");
beanFactory.registerBeanDefinition("classRoom", classRoom);
beanFactory.registerBeanPostProcessor(new BeanPostProcessor() {
private final Map<Object, Object> earlyProxyReferences = new HashMap<>();
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (earlyProxyReferences.remove(beanName) != bean) {
return wrapIfNecessary(bean, beanName);
}
return bean;
}
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
// earlyProxyReferences.put(beanName, bean);
// return wrapIfNecessary(bean, beanName);
return bean; // 如果返回原始bean,则无法处理循环依赖注入代理bean的业务(三级缓存)
}
private Object wrapIfNecessary(Object bean, String beanName) {
if (bean.getClass() == ClassRoom.class) {
return new ClassRoomProxy((ClassRoom) bean);
}
return bean;
}
});
ClassRoom classRoomInstance = (ClassRoom) beanFactory.getBean("classRoom");
System.out.println(beanFactory);
System.out.println("ioc容器内的ClassRoom = " + classRoomInstance);
System.out.println("ioc容器内的classRoom.student.classRoom = " + classRoomInstance.getStudent().getClassRoom());
}
}
复制代码
控制台输出:
Student init, classRoom.init = false
ClassRoom init, student.init = true
Exception in thread "main" java.lang.RuntimeException: Bean with name 'classRoom' has been injected into other beans student 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 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
复制代码
总结
本文循序渐进,通过手写代码+源码分析的方式,阐述了Spring循环依赖的三个Map的必要性,并拓展分析了SpringAop中循环依赖处理失败的原因。