问题的产生

  • 代码出现两个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
    @Component
    public class A {
    private B b;

    @Autowired
    public A(B b){
    this.b = b;
    }
    }

    @Component
    public class B {
    private A a;

    @Autowired
    public B(A a){
    this.a = a;
    }
    }

    public static void main(String[] args) {
    ConfigurableApplicationContext run = SpringApplication.run(Springboot.class, args);

    Object a = run.getBean("a");
    }
  • 我们在向容器获取A对象是出错了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    The dependencies of some of the beans in the application context form a cycle:

    ┌─────┐
    | a defined in file [D:\download\SpringBoot\springboot_demo05\target\classes\com\panther\springboot\mycompoent\A.class]
    ↑ ↓
    | b defined in file [D:\download\SpringBoot\springboot_demo05\target\classes\com\panther\springboot\mycompoent\B.class]
    └─────┘


    Action:

    Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

    解决办法

    方案一

    自己创建对象,不使用IOC自动注入

    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
    public class A {
    private B b;
    public A(){

    }

    public A(B b){
    this.b = b;
    }

    public void setB(B b) {
    this.b = b;
    }

    public B getB() {
    return b;
    }
    }

    public class B {
    private A a;

    public B(){

    }

    public B(A a){
    this.a = a;
    }

    public void setA(A a) {
    this.a = a;
    }

    public A getA() {
    return a;
    }
    }

    private static final Logger logger = Logger.getLogger("logger");

    public static void main(String[] args) {
    SpringApplication.run(Springboot.class, args);

    // Object a = run.getBean("a");
    A a = new A();
    B b = new B();

    b.setA(a);
    a.setB(b);
    logger.info(String.valueOf(a.getB() == b)); //true
    logger.info(String.valueOf(b.getA() == a)); //true
    logger.info(String.valueOf(a.getB()); //com.panther.test@B5947
    logger.info(String.valueOf(b.getA()); //com.panther.test@A5945
    }

    DEBUG分析

  • 发现在new a和b时 属性都是空的
  • 向下继续运行时,好像还是产生了循环依赖,但为什么a.getB()能输出B的实体类
  • 有点懵逼。。。

    百度的拼凑版本解释: jvm在new一个对象时,并不会创建这个对象,只是向内存申请了一段空间,属于半初始化的实体类。

    方案二

    Spring框架提供的解决办法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    @Component
    public class A {
    private B b;

    @Autowired
    public void setB(B b) {
    this.b = b;
    }
    }

    @Component
    public class B {
    private A a;

    @Autowired
    public void setA(A a) {
    this.a = a;
    }
    }

    public static void main(String[] args) {
    ConfigurableApplicationContext run = SpringApplication.run(Springboot.class, args);

    A a = run.getBean(A.class);
    B b = run.getBean(B.class);

    logger.info(String.valueOf(a.getB() == b)); //true
    logger.info(String.valueOf(b.getA() == a)); //true
    logger.info(b.getA());//com.panther.springboot.test.A@27e7c77f
    logger.info(a.getB());//com.panther.springboot.test.B@6f70a21b
    }


  • 开启循环引用配置
    1
    spring.main.allow-circular-references: true
  • spring 引入三级缓存去解决循环引用
    1
    2
    3
    4
    5
    6
    7
    8
    //一级缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    // 三级缓存 map中存放早期半初始化的的bean,并提前暴露
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    //二级缓存
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
  • 可以发现b实体类里的A属性是一个代理对象
    1
    2
    3
    4
    5
    6
    7
    if (specificInterceptors != DO_NOT_PROXY) {
    this.advisedBeans.put(cacheKey, Boolean.TRUE);
    Object proxy = createProxy(
    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    this.proxyTypes.put(cacheKey, proxy.getClass());
    return proxy;
    }

    方案三

    自己写一个代理去解决

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Component
    @Aspect
    public class MyAspectJ {

    @Before("execution(* com.panther.springboot.mycompoent.*.*(..))")
    public void before(){
    System.out.println("---------");
    }
    }