Spring循环依赖终结篇

Spring循环依赖终结篇

前言

写作背景:最近(2022-08-17)在准备面试换工作(base上海),不喜欢背八股文,网上关于Spring循环依赖的文章浅尝辄止,也罢,还是自己边看源码边写来梳理一下这个问题。

先给结论:Spring通过三个Map解决set方法注入的循环依赖问题:

  1. 第一层Map<String,Object> singletonObjects:暴露已经完全创建好的单例bean;
  2. 第二层Map<String,Object> earlySingletonObjects:暴露早期Bean,可以理解为刚new的实例,还未执行初始化;
  3. 第三层Map<String, ObjectFactory> singletonFactories:暴露创建早期Bean的工厂;

本文涉及前置知识储备包括:

  1. SpringIOC容器生命周期;
  2. SpringBean生命周期;
  3. SpringAOP创建代理对象;

本文通过结合源码手写代码的方式,一次搞定Spring循环依赖的原理。其优点在于:

  1. 将复杂类继承结构平铺,比普通源码分析容易理解;
  2. 忽略与循环依赖无关代码,集中攻克循环依赖原理;
  3. 从2个Map->3个Map->SpringAop层层递进,更深一步理解循环依赖;

注:手写代码忽略与循环依赖无关的所有细节,包括但不限于:BeanDefinition合并、FactoryBean、bean构造方法选择、线程安全、异常处理。但是能保证所有提到的Bean生命周期顺序是正确的

一、2个Map

Q:2个Map能解决循环依赖问题吗?

A:能。但有缺陷。

核心模型

1)BeanDefinition

BeanDefinition:构建Bean需要的元数据信息。

一般来说,Spring全家桶中BeanDefinition一般有三种来源:

  1. 基于xml配置的IOC容器:例如ClassPathXmlApplicationContext通过XmlBeanDefinitionReader加载xml文件中的bean定义;
  2. 基于注解配置的IOC容器:ConfigurationClassPostProcessor(BeanDefinitionRegistryPostProcessor)解析Configuration注解类的BeanDefinition,注册更多BeanDefinition(如@Bean、@Import等);
  3. 通过api直接注册BeanDefinition:比如springboot中通过AnnotatedBeanDefinitionReader.register方法,将启动类注册为BeanDefinition;

这里我们简化BeanDefinition模型,只包含下面两个属性:

  1. clazz:Bean的Class类型;
  2. 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包括:

  1. 部分Aware方法回调;
  2. BeanPostProcessor前置处理(@PostConstruct);
  3. InitializingBean回调;
  4. 自定义initMethod;
  5. 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,共涉及以下几个改动点:

  1. getSingleton:支持3级map;
  2. 不直接暴露早期bean,而是暴露早期bean的工厂ObjectFactory;
  3. initializeBean:在初始化阶段的最后,调用BeanPostProcessor.postProcessAfterInitialization,通常在这里将原始bean包装为代理bean;
  4. getSingletonFromLevel1Level2:如果发生循环依赖,在外层bean的创建过程中,没有做任何代理逻辑,那么取ObjectFactory产生的bean,可能是个早期暴露的代理bean(因为是由于内层依赖bean通过获取早期bean创建的);
  5. 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);
}
复制代码

这里要理解一下这段判断逻辑:

  1. bean:刚new出来的bean;
  2. exposedObject:由当前getBean(beanName)方法执行完beanPostProcessor.postProcessAfterInitialization得到的对象
  3. 如果bean==exposedObject,代表bean在本次getBean过程中并没有被包装
  4. 但是earlySingletonReference非空,代表在2级map中存在一个这个bean的早期被ObjectFactory暴露的bean,这意味着一定发生了循环依赖,导致当前bean被早期暴露被其他bean注入
  5. 那么要取被其他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阶段执行:

  1. postProcessBeforeInitialization:在执行init方法前被调用,如果返回null,阻断后续其他BeanPostProcessor处理postProcessBeforeInitialization,如果返回非原始bean,那就后续对非原始bean执行初始化;
  2. 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

  1. postProcessBeforeInstantiation:在bean被new之前调用,如果返回非空,直接跳过后续所有步骤,如果返回空,就当做什么事都没发生;
  2. postProcessAfterInstantiation:在populateBean阶段一开始被调用,如果某个postProcessAfterInstantiation方法返回false,将阻断后续populateBean流程,即不做依赖注入;
  3. 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

他的作用是:

  1. 筛选BeanDefinition是Advisor类型的;
  2. 筛选BeanDefinition.role=ROLE_INFRASTRUCTURE(框架内置的BeanDefinition);
  3. 对于当前正在创建的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中循环依赖处理失败的原因。

分类:
后端