Spring-让你彻底搞懂Spring如何解决循环依赖?

298 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

一、Spring容器启动流程

创建配置类

@ComponentScan(value = {"com.woniu"})
@Configuration
public class MyConfig {
}

创建一个普通类

package com.woniu.service;

import org.springframework.stereotype.Component;

@Component
public class Eservice {
}

创建一个启动类

public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

    }
}

在启动类的第三行打断点开始单步调试,进入spring的容器启动流程

1

2

3

4

5

6

7

8

这个方法非常重要,在循环依赖中对于同一个bean会多次调用,先从一级缓存中查询是否存在bean,若不存在,再从二级缓存中查询是否存在bean,如果还是没有找到,从三级缓存中查询singletonFactory,如果没有,则直接返回,若在三级缓存中找到singletonFactory,则调用工厂中的方法创建bean,然后在把bean添加到二级缓存中

9

什么是三级缓存

10

11

12

13

14.png

15

16

17

18

19.png

20

21.png

22

23

25

26

二、简单依赖注入

再创建一个普通类

package com.woniu.service;

import org.springframework.stereotype.Component;

@Component
public class Fservice {
}

修改一下Eservice,注入Fservice实例

package com.woniu.service;

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

@Component
public class Eservice {

    @Autowired
    private Fservice fservice;
}

27.png

28.png

29.png

30.png

31.png

32.png

33.png

在这个populateBean里面注入Fservice的实例到Eservice中

35

36

38

39

40

41

42

43

上面截图中的descriptor.resolveCandidate方法继续跟踪会进入到下面截图的方法,注意,这时候,需要从容器中查找Fservice的实例对象了

44

下面就正式进入创建一个Fservice对象的实例,并且添加到spring的容器中,也就是第一部分讲的spring容器启动流程

45

47

48

把创建好的fservice实例返回给刚才正在处理@Autowired的注解的代码

49

50

51

53

52

54

程序继续运行,把eservice也加入到一级缓存

55

56

三、AOP依赖注入

修改MyConfig

package com.woniu;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@ComponentScan(value = {"com.woniu"})
@EnableAspectJAutoProxy
@Configuration
public class MyConfig {
}

创建MyAop类

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;


@Component
@Aspect
public class MyAop {

    //用来定义切入点表达式的方法,方法名就是切入点表达式的名称
    @Pointcut("execution(* com.woniu.service..*.*(..))")
    public void pt1(){ }

    @Before("pt1()")
    public void Before() throws Throwable {
        System.out.println("before aop...");
    }
}

Eservice

package com.woniu.service;

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

@Component
public class Eservice {

    @Autowired
    private Fservice fservice;
}

Fservice

package com.woniu.service;

import org.springframework.stereotype.Component;

@Component
public class Fservice {
    public void print(){
        System.out.println("fservice");
    }
}

启动类

package com.woniu;

import com.woniu.service.*;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;


public class Application {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

    }
}

58

59.png

60.png

61.png

62.png

63.png

64.png

65.png

66.png

67.png

68.png

69.png

70.png

71.png

72.png

73.png

74.png

75.png

76.png

77.png

78.png

79.png

此时fservice的原生bean已经实例已经创建完成,initializeBean这个方法将会做初始化,在这里,将会给fservice生成一个代理对象

80.png

81.png

这个BeanPostProcessor会为Fservice生成代理对象,并且放置在二级缓存

82.png

earlyProxyReferences可以帮助判断是否已经生成过代理对象,wrapIfNecessary这个方法就是生成代理对象的方法

83.png

84.png

创建代理工厂

85.png

86.png

87.png 88.png fservice的代理对象已经被创建出来

89.png

90.png

91.png

initializeBean方法调用完毕,Fservicve代理对象创建完毕

92.png

93.png

94.png

95.png

96.png

此时的一级缓存如下

97.png

98.png

99.png

至此Eservice对象的initializeBean方法执行完毕,fservice的代理对象已经被注入到eservice中

100.png

101.png

四、循环依赖

  1. 非单例V.S非单例

  2. 单例构造函数V.S单例构造函数

  3. 单例构造函数V.S单例普通依赖注入

  4. 单例普通依赖注入V.S单例普通依赖注入

  5. AOP单例普通依赖注入V.SAOP单例普通依赖注入****

五、相关代码

相关代码