[Spring]用100行代码来模拟解决Spring的循环依赖问题

Abstract

好久没写文章了, 最近喜欢上了springboot, 也发现这个开发起来确实很方便和快捷。 包括spring kafka, jdbc, & actuator. 一天, 比较惊讶于spring如何优雅的解决循环依赖的问题的, 因为自己代码有时候也没注意, @Autowired 到处都是, 但是发现spring竟然可以正常工作,于是好奇的想了解下其实现。 所以就有了这个文章。

Spring如何解决循环依赖的?

官方文档:here

Circular dependencies
If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.
Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken/egg scenario).

这里面提到2点:
(1) 确实可能存在鸡生蛋 蛋生鸡的问题。 如果有 则抛出异常 BeanCurrentlyInCreationException
(2) 可以试用set/post process来解决(不使用构造器依赖)。

然后百度了一下, 这篇文章写的很不错:here
总结来说就是: 递归调用+一个巧妙地early exposed map.

动手实现一个简单的解决循环依赖的版本

我们主要的目录结构如下: 完整源码在here
在这里插入图片描述BeanA & BeanB 两个测试bean。

package com.example.beans.beans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BeanA {
   
   
    @Autowired
    private BeanB b;

    public BeanA() {
   
   
        System.out.println("A's no args construtor");
    }

    public void print(){
   
   
        System.out.println("bean a post construct is called");
        System.out.println("Is bean b is null in bean a 's post " + (b == null) + " bean a in current b(" + b.getBeana());
        b.printMe();
    }
}

package com.example.beans.beans;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class BeanB {
   
   

    @Autowired
    private BeanA beana;

    public BeanA getBeana() {
   
   
        return beana