1. AOP

1. Aop的题目说明要求

Spring的AOP顺序
Spring的循环依赖
AOP常用注解:

  • @Before 前置通知:目标方法之前执行
  • @After 后置通知:目标方法之后执行(始终执行)
  • @AfterReturning 返回后通知:执行方法结束前执行(异常不执行)
  • @AfterThrowing 异常通知:出现异常时候执行
  • @Around 环绕通知:环绕目标方法执行

面试题
你肯定知道spring,那说说aop的全部通知顺序springboot或springboot2对aop的执行顺序影响?
说说你使用AOP中碰到的坑?

2. spring4下的aop测试案例

新建Maven工程,pom.xml如下:

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>1.5.9.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.lun</groupId>
  12. <artifactId>HelloSpringBoot</artifactId>
  13. <version>1.0.0-SNAPSHOT</version>
  14. <packaging>jar</packaging>
  15. <name>HelloSpringBoot</name>
  16. <url>http://maven.apache.org</url>
  17. <properties>
  18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19. </properties>
  20. <dependencies>
  21. <!-- <version>1.5.9.RELEASE</version ch/qos/Logback/core/joran/spi/JoranException解决方案-->
  22. <dependency>
  23. <groupId>ch.qos.logback</groupId>
  24. <artifactId>logback-core</artifactId>
  25. <version>1.1.3</version>
  26. </dependency>
  27. <dependency>
  28. <groupId>ch.qos.logback</groupId>
  29. <artifactId>logback-access</artifactId>
  30. <version>1.1.3</version>
  31. </dependency>
  32. <dependency>
  33. <groupId>ch.qos.logback</groupId>
  34. <artifactId>logback-classic</artifactId>
  35. <version>1.1.3</version>
  36. </dependency>
  37. <!-- web+actuator -->
  38. <dependency>
  39. <groupId>org.springframework.boot</groupId>
  40. <artifactId>spring-boot-starter-web</artifactId>
  41. </dependency>
  42. <dependency>
  43. <groupId>org.springframework.boot</groupId>
  44. <artifactId>spring-boot-starter-actuator</artifactId>
  45. </dependency>
  46. <!-- SpringBootRedis整合依赖 -->
  47. <!--
  48. <dependency>
  49. <groupId>org.springframework.boot</groupId>
  50. <artifactId>spring-boot-starter-data-redis</artifactId>
  51. </dependency>
  52. -->
  53. <dependency>
  54. <groupId>org.apache.commons</groupId>
  55. <artifactId>commons-pool2</artifactId>
  56. </dependency>
  57. <!-- jedis -->
  58. <dependency>
  59. <groupId>redis.clients</groupId>
  60. <artifactId>jedis</artifactId>
  61. <version>3.1.0</version>
  62. </dependency>
  63. <!-- Spring Boot AOP技术-->
  64. <dependency>
  65. <groupId>org.springframework.boot</groupId>
  66. <artifactId>spring-boot-starter-aop</artifactId>
  67. </dependency>
  68. <!-- redisson -->
  69. <dependency>
  70. <groupId>org.redisson</groupId>
  71. <artifactId>redisson</artifactId>
  72. <version>3.13.4</version>
  73. </dependency>
  74. <!-- 一般通用基础配置 -->
  75. <dependency>
  76. <groupId>org.springframework.boot</groupId>
  77. <artifactId>spring-boot-devtools</artifactId>
  78. <scope>runtime</scope>
  79. <optional>true</optional>
  80. </dependency>
  81. <dependency>
  82. <groupId>org.projectlombok</groupId>
  83. <artifactId>lombok</artifactId>
  84. <optional>true</optional>
  85. </dependency>
  86. <dependency>
  87. <groupId>org.springframework.boot</groupId>
  88. <artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
  89. <exclusions>
  90. <exclusion>
  91. <groupId>org.junit.vintage</groupId>
  92. <artifactId>junit-vintage-engine</artifactId>
  93. </exclusion>
  94. </exclusions>
  95. </dependency>
  96. </dependencies>
  97. <build>
  98. <plugins>
  99. <plugin>
  100. <groupId>org.springframework.boot</groupId>
  101. <artifactId>spring-boot-maven-plugin</artifactId>
  102. </plugin>
  103. </plugins>
  104. </build>
  105. </project>

启动类

  1. import org.springframework.boot.SpringApplication;
  2. import org.springframework.boot.autoconfigure.SpringBootApplication;
  3. @SpringBootApplication
  4. public class MainApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(MainApplication.class, args);
  7. }
  8. }

接口CalcService

  1. public interface CalcService {
  2. public int div(int x, int y);
  3. }

接口实现类CalcServiceImpl新加@Service

  1. import org.springframework.stereotype.Service;
  2. @Service
  3. public class CalcServiceImpl implements CalcService {
  4. @Override
  5. public int div(int x, int y) {
  6. int result = x / y;
  7. System.out.println("===>CalcServiceImpl被调用,计算结果为:" + result);
  8. return result;
  9. }
  10. }

新建一个切面类MyAspect并为切面类新增两个注解:

  • @Aspect 指定一个类为切面类
  • @Component 纳入Spring容器管理 ```java import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component;

@Aspect @Component public class MyAspect { @Before(“execution(public int com.lun.interview.service.CalcServiceImpl.(..))”) public void beforeNotify() { System.out.println(“*@Before我是前置通知”); }

  1. @After("execution(public int com.lun.interview.service.CalcServiceImpl.*(..))")
  2. public void afterNotify() {
  3. System.out.println("********@After我是后置通知");
  4. }
  5. @AfterReturning("execution(public int com.lun.interview.service.CalcServiceImpl.*(..))")
  6. public void afterReturningNotify() {
  7. System.out.println("********@AfterReturning我是返回后通知");
  8. }
  9. @AfterThrowing(" execution(public int com.lun.interview.service.CalcServiceImpl.*(..))")
  10. public void afterThrowingNotify() {
  11. System.out.println("********@AfterThrowing我是异常通知");
  12. }
  13. @Around(" execution(public int com.lun.interview.service.CalcServiceImpl.*(..))")
  14. public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
  15. Object retvalue = null;
  16. System.out.println("我是环绕通知之前AAA");
  17. retvalue = proceedingJoinPoint.proceed();
  18. System.out.println("我是环绕通知之后BBB");
  19. return retvalue ;
  20. }

}

  1. 测试类
  2. ```java
  3. import javax.annotation.Resource;
  4. import org.junit.Test;
  5. import org.junit.runner.RunWith;
  6. import org.springframework.boot.SpringBootVersion;
  7. import org.springframework.boot.test.context.SpringBootTest;
  8. import org.springframework.core.SpringVersion;
  9. import org.springframework.test.context.junit4.SpringRunner;
  10. import com.lun.interview.service.CalcService;
  11. @SpringBootTest
  12. @RunWith(SpringRunner.class)
  13. public class AopTest {
  14. @Resource
  15. private CalcService calcService;
  16. @Test
  17. public void testAop4() {
  18. System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
  19. SpringVersion.getVersion(), SpringBootVersion.getVersion()));
  20. calcService.div(10, 2);
  21. }
  22. }

3. spring4下的aop测试结果

继续上节
输出结果:

  1. Spring Verision : 4.3.13.RELEASE, Sring Boot Version : 1.5.9.RELEASE.
  2. 我是环绕通知之前AAA
  3. ********@Before我是前置通知
  4. ===>CalcServiceImpl被调用,计算结果为:5
  5. 我是环绕通知之后BBB
  6. ********@After我是后置通知
  7. ********@AfterReturning我是返回后通知

修改测试类,让其抛出算术异常类:

  1. @SpringBootTest
  2. @RunWith(SpringRunner.class)
  3. public class AopTest {
  4. @Resource
  5. private CalcService calcService;
  6. @Test
  7. public void testAop4() {
  8. System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
  9. SpringVersion.getVersion(), SpringBootVersion.getVersion()));
  10. //calcService.div(10, 2);
  11. calcService.div(10, 0);//将会抛异常
  12. }
  13. }

输出结果:

  1. Spring Verision : 4.3.13.RELEASE, Sring Boot Version : 1.5.9.RELEASE.
  2. 我是环绕通知之前AAA
  3. ********@Before我是前置通知
  4. ********@After我是后置通知
  5. ********@AfterThrowing我是异常通知
  6. java.lang.ArithmeticException: / by zero
  7. at com.lun.interview.service.CalcServiceImpl.div(CalcServiceImpl.java:10)
  8. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  9. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  10. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  11. at java.lang.reflect.Method.invoke(Method.java:498)
  12. ...

小结
AOP执行顺序:

  • 正常情况下:@Before前置通知——->@After后置通知——->@AfterRunning正常返回
  • 异常情况下:@Before前置通知——->@After后置通知——->@AfterThrowing方法异常

4. spring5下的aop测试

修改POM

  1. <project xmlns="http://maven.apache.org/POM/4.0.0"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.3.3.RELEASE</version>
  9. <!-- 1.5.9.RELEASE -->
  10. <relativePath/> <!-- lookup parent from repository -->
  11. </parent>
  12. <groupId>com.lun</groupId>
  13. <artifactId>HelloSpringBoot</artifactId>
  14. <version>1.0.0-SNAPSHOT</version>
  15. <packaging>jar</packaging>
  16. <name>HelloSpringBoot</name>
  17. <url>http://maven.apache.org</url>
  18. <properties>
  19. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  20. </properties>
  21. <dependencies>
  22. <!-- web+actuator -->
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-web</artifactId>
  26. </dependency>
  27. <dependency>
  28. <groupId>org.springframework.boot</groupId>
  29. <artifactId>spring-boot-starter-actuator</artifactId>
  30. </dependency>
  31. <!-- SpringBootRedis整合依赖 -->
  32. <!--
  33. <dependency>
  34. <groupId>org.springframework.boot</groupId>
  35. <artifactId>spring-boot-starter-data-redis</artifactId>
  36. </dependency>
  37. -->
  38. <dependency>
  39. <groupId>org.apache.commons</groupId>
  40. <artifactId>commons-pool2</artifactId>
  41. </dependency>
  42. <!-- jedis -->
  43. <dependency>
  44. <groupId>redis.clients</groupId>
  45. <artifactId>jedis</artifactId>
  46. <version>3.1.0</version>
  47. </dependency>
  48. <!-- Spring Boot AOP技术-->
  49. <dependency>
  50. <groupId>org.springframework.boot</groupId>
  51. <artifactId>spring-boot-starter-aop</artifactId>
  52. </dependency>
  53. <!-- redisson -->
  54. <dependency>
  55. <groupId>org.redisson</groupId>
  56. <artifactId>redisson</artifactId>
  57. <version>3.13.4</version>
  58. </dependency>
  59. <!-- 一般通用基础配置 -->
  60. <dependency>
  61. <groupId>org.springframework.boot</groupId>
  62. <artifactId>spring-boot-devtools</artifactId>
  63. <scope>runtime</scope>
  64. <optional>true</optional>
  65. </dependency>
  66. <dependency>
  67. <groupId>org.projectlombok</groupId>
  68. <artifactId>lombok</artifactId>
  69. <optional>true</optional>
  70. </dependency>
  71. <dependency>
  72. <groupId>org.springframework.boot</groupId>
  73. <artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
  74. <exclusions>
  75. <exclusion>
  76. <groupId>org.junit.vintage</groupId>
  77. <artifactId>junit-vintage-engine</artifactId>
  78. </exclusion>
  79. </exclusions>
  80. </dependency>
  81. </dependencies>
  82. <build>
  83. <plugins>
  84. <plugin>
  85. <groupId>org.springframework.boot</groupId>
  86. <artifactId>spring-boot-maven-plugin</artifactId>
  87. </plugin>
  88. </plugins>
  89. </build>
  90. </project>

修改测试类

  1. import javax.annotation.Resource;
  2. import org.junit.jupiter.api.Test;
  3. //import org.junit.Test;
  4. //import org.junit.runner.RunWith;
  5. import org.springframework.boot.SpringBootVersion;
  6. import org.springframework.boot.test.context.SpringBootTest;
  7. import org.springframework.core.SpringVersion;
  8. //import org.springframework.test.context.junit4.SpringRunner;
  9. import com.lun.interview.service.CalcService;
  10. @SpringBootTest
  11. //@RunWith(SpringRunner.class)
  12. public class AopTest {
  13. @Resource
  14. private CalcService calcService;
  15. @Test
  16. public void testAop5() {
  17. System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
  18. SpringVersion.getVersion(), SpringBootVersion.getVersion()));
  19. System.out.println();
  20. calcService.div(10, 2);
  21. //calcService.div(10, 0);
  22. }
  23. }

输出结果

  1. Spring Verision : 5.2.8.RELEASE, Sring Boot Version : 2.3.3.RELEASE.
  2. 我是环绕通知之前AAA
  3. ********@Before我是前置通知
  4. ===>CalcServiceImpl被调用,计算结果为:5
  5. ********@AfterReturning我是返回后通知
  6. ********@After我是后置通知
  7. 我是环绕通知之后BBB

修改测试类,让其抛出算术异常类:

  1. @SpringBootTest
  2. public class AopTest {
  3. @Resource
  4. private CalcService calcService;
  5. ...
  6. @Test
  7. public void testAop5() {
  8. System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
  9. SpringVersion.getVersion(), SpringBootVersion.getVersion()));
  10. System.out.println();
  11. calcService.div(10, 2);
  12. //calcService.div(10, 0);
  13. }
  14. }

输出结果

  1. Spring Verision : 5.2.8.RELEASE, Sring Boot Version : 2.3.3.RELEASE.
  2. 我是环绕通知之前AAA
  3. ********@Before我是前置通知
  4. ********@AfterThrowing我是异常通知
  5. ********@After我是后置通知
  6. java.lang.ArithmeticException: / by zero
  7. at com.lun.interview.service.CalcServiceImpl.div(CalcServiceImpl.java:10)
  8. at com.lun.interview.service.CalcServiceImpl$$FastClassBySpringCGLIB$$355acbc4.invoke(<generated>)
  9. at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
  10. at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)

2. 循环依赖

1. spring循环依赖题目说明

恶心的大厂面试题

  • 你解释下spring中的三级缓存?
  • 三级缓存分别是什么?三个Map有什么异同?
  • 什么是循环依赖?请你谈谈?看过spring源码吗?
  • 如何检测是否存在循环依赖?实际开发中见过循环依赖的异常吗?
  • 多例的情况下,循环依赖问题为什么无法解决?

    什么是循环依赖?

多个bean之间相互依赖,形成了一个闭环。比如:A依赖于B、B依赖于C、C依赖于A。
通常来说,如果问Spring容器内部如何解决循环依赖,一定是指默认的单例Bean中,属性互相引用的场景。
图片.png
两种注入方式对循环依赖的影响
循环依赖官网说明

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-and-egg scenario). link

结论

我们AB循环依赖问题只要A的注入方式是setter且singleton ,就不会有循环依赖问题。

三级缓存

首先看下Spring创建一个bean的基本流程:
创建该实例的原始对象 —> 进行自动装配 —> AOP代理处理 —> 完成bean的创建并加入单例池(即一级缓存)
但是当有循环依赖的时候,Spring是如何解决的呢?
主要原理是利用三级缓存机制:
图片.png
singletonObjects: 一级缓存,也就是我们平常理解的单例池。
singletonFactories: 二级缓存,存储的是单例工厂。
earlySingletonObjects: 三级缓存,存储正在创建中的单例对象。

为什么要设立三级缓存呢?

其实当不需要实现AOP的时候,解决循环依赖不用三级缓存机制,也不用单例工厂,二级缓存就足以实现。
第一级:singletonObjects
第二级:earlySingletonObjects

不需要实现AOP时Spring解决循环依赖基本流程:

假设单例一与单例二相互依赖对方并且此时都没有加入到单例池
1 创建单例一
2 将单例一加入earlySingletonObjects缓存
3 自动装配单例二
4 判断单例二在earlySingletonObjects缓存是否存在
5 不存在则创建单例二
6 将单例二并且加入earlySingletonObjects缓存
7 自动装配单例一
8 判断单例一在earlySingletonObjects缓存是否存在
9 明显第2步已经加入earlySingletonObjects缓存
10 注入成功,单例2创建完成并加入singletonObjects单例池
11 注入成功,单例1创建完成并加入singletonObjects单例池

那为什么要设立三级缓存呢?

这是因为当我们需要使用AOP时,将会对原始对象进行代理,因此最后的对象将是代理对象而不是原始对象!

所以按照二级缓存的步骤进行创建的话将会造成一个问题:

第2步加入到二级缓存中的对象是原始对象,导致第7步自动装配到到单例2中的单例1对象是原始对象,这个对象还没有完成AOP的处理。如果此时完成单例2的创建,之后在完成进行单例1的创建时,单例1对原始对象进行AOP处理,将导致最终的单例1对象不是之前自动装配到单例2中的单例1对象。

所以Spring选择使用三级缓存来解决这个问题:singletonFactories缓存中的对象是一个单例工厂,该工厂可以将原始对象进行AOP处理。

getSingleton方法的大概处理过程为:
1 判断singletonObjects单例池中是否存在,存在则返回
2 不存在则判断earlySingletonObjects缓存中是否存在,存在则返回
3 不存在则判断singletonFactories缓存中是否存在,不存在则返回null
4 存在则通过该存储工厂创建出最终的bean
5 将该bean加入earlySingletonObjects缓存并从singletonFactories缓存中中移除

存在三级缓存机制下处理流程如下:
假设单例1与单例2相互依赖对方并且此时都没有加入到单例池
1 创建单例一
2 创建单例一的工厂对象并且加入singletonFactories缓存
3 自动装配单例二
4 通过 getSingleton方法 判断单例二在三级缓存中是否存在
5 明显此时单例一不存在,所以创建单例二
6 创建单例二的工厂并且加入singletonFactories缓存
7 自动装配单例一
8 通过 getSingleton方法 判断单例一在三级缓存中是否存在
9 明显第2步已经加入singletonFactories缓存
10 单例二进行AOP处理
11 注入单例一成功,单例二创建完成并加入singletonObjects单例池
12 注入单例二成功,单例一创建完成并加入singletonObjects单例池

以上步骤Spring就完美解决了循环依赖的问题!

检测循环依赖

spring针对循环依赖问题 不能完全解决 对于不能解决的只能检测到并抛出异常
1. spring针对构造器方法的 单实例对象和原型对象是无法解决循环依赖问题的 先说结论, 针对单例对象 getSingleton方法中 有个beforeSingletonCreation 方法 这个方法是用来检测循环依赖的 原型对象 isPrototypeCurrentlyInCreation方法beforePrototypeCreation方法配合检测循环依赖
注: inCreationCheckExclusions和singletonsCurrentlyInCreation 是两个set

  1. 1 protected void beforeSingletonCreation(String beanName) {
  2. 2 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
  3. 3 throw new BeanCurrentlyInCreationException(beanName);
  4. 4 }
  5. 5 }

以AB两个单例对象举例,

  1. Class A(){
  2. A(B b){
  3. }
  4. }
  5. Class B(){
  6. B(A a){
  7. }
  8. }

对象A在实例化的时候(getBean方法),会先执行beforeSingletonCreation方法 吧自己的beanName放入set中,然后去执行实例化 解析构造器方法的时候发现 需要用到对象B ,所以去getBean(B)
B对象在一级缓存是没有的(因为是还未实例化),所以去创建单例B,会执行和A一样的操作,把自己的beanName放入set中,然后解析构造器的时候发现依赖A对象,去一级缓存获取是没有的(因为A对象实例化还未完成 未放入到一级缓存中)
所以去实例化A 放入set的时候 发现已经存在及会抛出异常

  1. 1 protected boolean isPrototypeCurrentlyInCreation(String beanName) {
  2. 2 Object curVal = this.prototypesCurrentlyInCreation.get();
  3. 3 return (curVal != null &&
  4. 4 (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
  5. 5 }
  1. protected void beforePrototypeCreation(String beanName) {
  2. Object curVal = this.prototypesCurrentlyInCreation.get();
  3. if (curVal == null) {
  4. this.prototypesCurrentlyInCreation.set(beanName);
  5. }
  6. else if (curVal instanceof String) {
  7. Set<String> beanNameSet = new HashSet<>(2);
  8. beanNameSet.add((String) curVal);
  9. beanNameSet.add(beanName);
  10. this.prototypesCurrentlyInCreation.set(beanNameSet);
  11. }
  12. else {
  13. Set<String> beanNameSet = (Set<String>) curVal;
  14. beanNameSet.add(beanName);
  15. }
  16. }

对象在实例化之前会先调用isPrototypeCurrentlyInCreation方法,如果set中没有继续执行,实例化的时候调用beforePrototypeCreation方法 放入set中,在之后的循环依赖中 查询到set中有自己的beanName 则抛出异常

那么spring对set方法注入 是如何解决循环依赖问题的呢,一样先说结论 是使用第三级缓存来解决的
1. 在实例化bean A的时候 先去查看一级缓存中是否有(执行的方法getSingleton)
并且会查询currentlyCreationSet中有没有
2. 处理一下depend-on依赖 或者检查一下是否是抽象等等安全性校验
3.把单实例的A 的beanName放入currentlyCreationSet中
4. 实例化A对象因为是set注入 所以这时使用的空参构造 去反射出实例对象 得到一个早期对象(未进行属性注入的对象,未执行init方法调用,未进行后处理器处理,早期对象和处理后的对象的内存地址是一样的aop对象除外)
5. 早期实例A封装到objectFactory对象中,放入三级缓存
6. 进行依赖注入 依赖注入的时候发现依赖Bean B 再去执行getBean方法 重复执行1-6步 拿到一个B的早期对象objectFactory 放入到三级缓存
7. 处理B的依赖注入 发现B依赖了A 执行getbean方法 从1开始执行 去一级缓存查询没有 去currentlyCreationSet查询有A 去三级缓存中拿到A的早期实例 放入二级缓存清除三级缓存中的A早期实例并返回这个早期实例 B继续执行完成了B对象的实例 放入一级缓存中 清除掉二三级缓存相关数据
8. 继续执行A的实例化步骤 A完成实例化 存入一级缓存 清除二三级缓存相关数据

  1. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  2. Object singletonObject = this.singletonObjects.get(beanName);
  3. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  4. synchronized (this.singletonObjects) {
  5. singletonObject = this.earlySingletonObjects.get(beanName);
  6. if (singletonObject == null && allowEarlyReference) {
  7. ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  8. if (singletonFactory != null) {
  9. singletonObject = singletonFactory.getObject();
  10. this.earlySingletonObjects.put(beanName, singletonObject);
  11. this.singletonFactories.remove(beanName);
  12. }
  13. }
  14. }
  15. }
  16. return singletonObject;
  17. }

2. spring循环依赖纯java代码验证案例

Spring容器循环依赖报错演示BeanCurrentlylnCreationException
循环依赖现象在spring容器中注入依赖的对象,有2种情况

  • 构造器方式注入依赖(不可行)
  • 以set方式注入依赖(可行)

构造器方式注入依赖(不可行)

  1. @Component
  2. public class ServiceB{
  3. private ServiceA serviceA;
  4. public ServiceB(ServiceA serviceA){
  5. this.serviceA = serviceA;
  6. }
  7. }
  1. @Component
  2. public class ServiceA{
  3. private ServiceB serviceB;
  4. public ServiceA(ServiceB serviceB){
  5. this.serviceB = serviceB;
  6. }
  7. }
  1. public class ClientConstructor{
  2. public static void main(String[] args){
  3. new ServiceA(new ServiceB(new ServiceA()));//这会抛出编译异常
  4. }
  5. }

以set方式注入依赖(可行)

  1. @Component
  2. public class ServiceBB{
  3. private ServiceAA serviceAA;
  4. public void setServiceAA(ServiceAA serviceAA){
  5. this.serviceAA = serviceAA;
  6. System.out.println("B里面设置了A");
  7. }
  8. }
  1. @Component
  2. public class ServiceAA{
  3. private ServiceBB serviceBB;
  4. public void setServiceBB(ServiceBB serviceBB){
  5. this.serviceBB = serviceBB;
  6. System.out.println("A里面设置了B");
  7. }
  8. }
  1. public class ClientSet{
  2. public static void main(String[] args){
  3. //创建serviceAA
  4. ServiceAA a = new ServiceAA();
  5. //创建serviceBB
  6. ServiceBB b = new ServiceBB();
  7. //将serviceA入到serviceB中
  8. b.setServiceAA(a);
  9. //将serviceB法入到serviceA中
  10. a.setServiceBB(b);
  11. }
  12. }

输出结果:

  1. B里面设置了A
  2. A里面设置了B

3. spring循环依赖bug演示

beans:A,B

  1. public class A {
  2. private B b;
  3. public B getB() {
  4. return b;
  5. }
  6. public void setB(B b) {
  7. this.b = b;
  8. System.out.println("A call setB.");
  9. }
  10. }
  1. public class B {
  2. private A a;
  3. public A getA() {
  4. return a;
  5. }
  6. public void setA(A a) {
  7. this.a = a;
  8. System.out.println("B call setA.");
  9. }
  10. }

运行类

  1. import org.springframework.context.ApplicationContext;
  2. import org.springframework.context.support.ClassPathXmlApplicationContext;
  3. public class ClientSpringContainer {
  4. public static void main(String[] args) {
  5. ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
  6. A a = context.getBean("a", A.class);
  7. B b = context.getBean("b", B.class);
  8. }
  9. }

默认的单例(Singleton)的场景是支持循环依赖的,不报错
beans.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context-4.0.xsd
  10. http://www.springframework.org/schema/tx
  11. http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
  12. http://www.springframework.org/schema/aop
  13. http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
  14. <bean id="a" class="com.lun.interview.circular.A">
  15. <property name="b" ref="b"></property>
  16. </bean>
  17. <bean id="b" class="com.lun.interview.circular.B">
  18. <property name="a" ref="a"></property>
  19. </bean>
  20. </beans>

输出结果

  1. 00:00:25.649 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6d86b085
  2. 00:00:25.828 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [beans.xml]
  3. 00:00:25.859 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'a'
  4. 00:00:25.875 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'b'
  5. B call setA.
  6. A call setB.

原型(Prototype)的场景是不支持循环依赖的,会报错
beans.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context-4.0.xsd
  10. http://www.springframework.org/schema/tx
  11. http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
  12. http://www.springframework.org/schema/aop
  13. http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
  14. <bean id="a" class="com.lun.interview.circular.A" scope="prototype">
  15. <property name="b" ref="b"></property>
  16. </bean>
  17. <bean id="b" class="com.lun.interview.circular.B" scope="prototype">
  18. <property name="a" ref="a"></property>
  19. </bean>
  20. </beans>

输出结果

  1. 00:01:39.904 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6d86b085
  2. 00:01:40.062 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [beans.xml]
  3. Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'a' defined in class path resource [beans.xml]: Cannot resolve reference to bean 'b' while setting bean property 'b'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [beans.xml]: Cannot resolve reference to bean 'a' while setting bean property 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
  4. at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342)
  5. at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113)
  6. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1697)
  7. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1442)
  8. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
  9. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
  10. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:342)
  11. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
  12. at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1115)
  13. at com.lun.interview.circular.ClientSpringContainer.main(ClientSpringContainer.java:10)
  14. Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [beans.xml]: Cannot resolve reference to bean 'a' while setting bean property 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
  15. at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342)
  16. at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113)
  17. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1697)
  18. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1442)
  19. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
  20. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
  21. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:342)
  22. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
  23. at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
  24. ... 9 more
  25. Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
  26. at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:268)
  27. at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
  28. at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
  29. ... 17 more

重要结论(spring内部通过3级缓存来解决循环依赖) - DefaultSingletonBeanRegistry

只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。

第一级缓存(也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象。
第二级缓存:earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完。
第三级缓存:Map> singletonFactories,存放可以生成Bean的工厂。

  1. package org.springframework.beans.factory.support;
  2. ...
  3. public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  4. ...
  5. /** Cache of singleton objects: bean name to bean instance. */
  6. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  7. /** Cache of singleton factories: bean name to ObjectFactory. */
  8. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  9. /** Cache of early singleton objects: bean name to bean instance. */
  10. private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  11. ...
  12. }

4. spring循环依赖debug前置知识

实例化 - 内存中申请一块内存空间,如同租赁好房子,自己的家当还未搬来。
初始化属性填充 - 完成属性的各种赋值,如同装修,家具,家电进场。
3个Map和四大方法,总体相关对象
图片.png
第一层 singletonObjects存放的是已经初始化好了的Bean,
第二层 earlySingletonObjects存放的是实例化了,但是未初始化的Bean,
第三层 singletonFactories存放的是FactoryBean。假如A类实现了FactoryBean,那么依赖注入的时候不是A类,而是A类产生的Bean

  1. package org.springframework.beans.factory.support;
  2. ...
  3. public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  4. ...
  5. /**
  6. 单例对象的缓存:bean名称—bean实例,即:所谓的单例池。
  7. 表示已经经历了完整生命周期的Bean对象
  8. 第一级缓存
  9. */
  10. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  11. /**
  12. 早期的单例对象的高速缓存: bean名称—bean实例。
  13. 表示 Bean的生命周期还没走完(Bean的属性还未填充)就把这个 Bean存入该缓存中也就是实例化但未初始化的 bean放入该缓存里
  14. 第二级缓存
  15. */
  16. private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  17. /**
  18. 单例工厂的高速缓存:bean名称—ObjectFactory
  19. 表示存放生成 bean的工厂
  20. 第三级缓存
  21. */
  22. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  23. ...
  24. }

A / B两对象在三级缓存中的迁移说明

  1. A创建过程中需要B,于是A将自己放到三级缓里面,去实例化B。
  2. B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A。
  3. B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态),然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。 ```java @FunctionalInterface public interface ObjectFactory {

    T getObject() throws BeansException;

}

  1. <a name="Qd7bP"></a>
  2. ## 5. spring循环依赖debug源码
  3. DEBUG一步一步来,scope默认为singleton<br />从运行类启航
  4. ```java
  5. import org.springframework.context.ApplicationContext;
  6. import org.springframework.context.support.ClassPathXmlApplicationContext;
  7. public class ClientSpringContainer {
  8. public static void main(String[] args) {
  9. ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
  10. A a = context.getBean("a", A.class);
  11. B b = context.getBean("b", B.class);
  12. }
  13. }
  1. package org.springframework.context.support;
  2. ...
  3. public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
  4. public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
  5. this(new String[] {configLocation}, true, null);
  6. }
  7. public ClassPathXmlApplicationContext(
  8. String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
  9. throws BeansException {
  10. super(parent);
  11. setConfigLocations(configLocations);
  12. if (refresh) {
  13. //源于AbstractXmlApplicationContext
  14. //->AbstractRefreshableConfigApplicationContext
  15. //->AbstractRefreshableApplicationContext
  16. //->AbstractApplicationContext的refresh()
  17. refresh();
  18. }
  19. }
  20. }
  1. package org.springframework.context.support;
  2. ...
  3. public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
  4. ...
  5. }
  1. package org.springframework.context.support;
  2. ...
  3. public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
  4. implements BeanNameAware, InitializingBean {
  5. ...
  6. }
  1. package org.springframework.context.support;
  2. ...
  3. public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
  4. ...
  5. }
  1. package org.springframework.context.support;
  2. ...
  3. public abstract class AbstractApplicationContext extends DefaultResourceLoader
  4. implements ConfigurableApplicationContext {
  5. ...
  6. @Override
  7. public void refresh() throws BeansException, IllegalStateException {
  8. synchronized (this.startupShutdownMonitor) {
  9. // Prepare this context for refreshing.
  10. prepareRefresh();
  11. // Tell the subclass to refresh the internal bean factory.
  12. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  13. // Prepare the bean factory for use in this context.
  14. prepareBeanFactory(beanFactory);
  15. try {
  16. // Allows post-processing of the bean factory in context subclasses.
  17. postProcessBeanFactory(beanFactory);
  18. // Invoke factory processors registered as beans in the context.
  19. invokeBeanFactoryPostProcessors(beanFactory);
  20. // Register bean processors that intercept bean creation.
  21. registerBeanPostProcessors(beanFactory);
  22. // Initialize message source for this context.
  23. initMessageSource();
  24. // Initialize event multicaster for this context.
  25. initApplicationEventMulticaster();
  26. // Initialize other special beans in specific context subclasses.
  27. onRefresh();
  28. // Check for listener beans and register them.
  29. registerListeners();
  30. --------------->//<---------------------重点关注点是这里
  31. // Instantiate all remaining (non-lazy-init) singletons.
  32. finishBeanFactoryInitialization(beanFactory);
  33. // Last step: publish corresponding event.
  34. finishRefresh();
  35. }
  36. catch (BeansException ex) {
  37. if (logger.isWarnEnabled()) {
  38. logger.warn("Exception encountered during context initialization - " +
  39. "cancelling refresh attempt: " + ex);
  40. }
  41. // Destroy already created singletons to avoid dangling resources.
  42. destroyBeans();
  43. // Reset 'active' flag.
  44. cancelRefresh(ex);
  45. // Propagate exception to caller.
  46. throw ex;
  47. }
  48. finally {
  49. // Reset common introspection caches in Spring's core, since we
  50. // might not ever need metadata for singleton beans anymore...
  51. resetCommonCaches();
  52. }
  53. }
  54. }
  55. /**
  56. * Finish the initialization of this context's bean factory,
  57. * initializing all remaining singleton beans.
  58. */
  59. protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  60. // Initialize conversion service for this context.
  61. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
  62. beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
  63. beanFactory.setConversionService(
  64. beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
  65. }
  66. // Register a default embedded value resolver if no bean post-processor
  67. // (such as a PropertyPlaceholderConfigurer bean) registered any before:
  68. // at this point, primarily for resolution in annotation attribute values.
  69. if (!beanFactory.hasEmbeddedValueResolver()) {
  70. beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
  71. }
  72. // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
  73. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
  74. for (String weaverAwareName : weaverAwareNames) {
  75. getBean(weaverAwareName);
  76. }
  77. // Stop using the temporary ClassLoader for type matching.
  78. beanFactory.setTempClassLoader(null);
  79. // Allow for caching all bean definition metadata, not expecting further changes.
  80. beanFactory.freezeConfiguration();
  81. ------->//<---------------------重点关注点是这里
  82. // Instantiate all remaining (non-lazy-init) singletons.
  83. beanFactory.preInstantiateSingletons();
  84. }
  85. }

接着上文的beanFactory.preInstantiateSingletons()

beanFactory是ConfigurableListableBeanFactory

DefaultListableBeanFactory实现了ConfigurableListableBeanFactory接口

  1. public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
  2. implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
  3. ...
  4. @Override
  5. public void preInstantiateSingletons() throws BeansException {
  6. if (logger.isTraceEnabled()) {
  7. logger.trace("Pre-instantiating singletons in " + this);
  8. }
  9. // Iterate over a copy to allow for init methods which in turn register new bean definitions.
  10. // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
  11. List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
  12. //根据上下文beanNames为[a, b]
  13. // Trigger initialization of all non-lazy singleton beans...
  14. for (String beanName : beanNames) {//遍历a,b
  15. RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
  16. if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
  17. if (isFactoryBean(beanName)) {
  18. Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
  19. if (bean instanceof FactoryBean) {
  20. FactoryBean<?> factory = (FactoryBean<?>) bean;
  21. boolean isEagerInit;
  22. if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
  23. isEagerInit = AccessController.doPrivileged(
  24. (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
  25. getAccessControlContext());
  26. }
  27. else {
  28. isEagerInit = (factory instanceof SmartFactoryBean &&
  29. ((SmartFactoryBean<?>) factory).isEagerInit());
  30. }
  31. if (isEagerInit) {
  32. getBean(beanName);
  33. }
  34. }
  35. }
  36. else {
  37. ------------------->//<---------------------重点关注点是这里
  38. //源于AbstractAutowireCapableBeanFactory
  39. //->AbstractBeanFactory的getBean()
  40. getBean(beanName);
  41. }
  42. }
  43. }
  44. ------------------------------下面可略读---------------------------
  45. // Trigger post-initialization callback for all applicable beans...
  46. for (String beanName : beanNames) {
  47. Object singletonInstance = getSingleton(beanName);
  48. if (singletonInstance instanceof SmartInitializingSingleton) {
  49. SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
  50. if (System.getSecurityManager() != null) {
  51. AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
  52. smartSingleton.afterSingletonsInstantiated();
  53. return null;
  54. }, getAccessControlContext());
  55. }
  56. else {
  57. smartSingleton.afterSingletonsInstantiated();
  58. }
  59. }
  60. }
  61. }
  62. }
  1. package org.springframework.beans.factory.support;
  2. ...
  3. public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
  4. implements AutowireCapableBeanFactory {
  5. ...
  6. }
  1. package org.springframework.beans.factory.support;
  2. ...
  3. public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
  4. ...
  5. @Override
  6. public Object getBean(String name) throws BeansException {
  7. return doGetBean(name, null, null, false);
  8. }
  9. @SuppressWarnings("unchecked")
  10. protected <T> T doGetBean(
  11. String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
  12. throws BeansException {
  13. //name为a
  14. String beanName = transformedBeanName(name);
  15. Object bean;
  16. // Eagerly check singleton cache for manually registered singletons.
  17. ------->//<---------------------重点关注点是这里
  18. ------->//<---------------------重点关注点是这里
  19. ------->//<---------------------重点关注点是这里
  20. //源于FactoryBeanRegistrySupport
  21. //->DefaultSingletonBeanRegistry的getSingleton()
  22. //DefaultSingletonBeanRegistry也就是上文谈论的三级缓存所在类
  23. //本章节末有getSingleton()源码
  24. //最后getSingleton返回null
  25. Object sharedInstance = getSingleton(beanName);
  26. //sharedInstance为null,下面if语块不执行
  27. if (sharedInstance != null && args == null) {
  28. if (logger.isTraceEnabled()) {
  29. if (isSingletonCurrentlyInCreation(beanName)) {
  30. logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
  31. "' that is not fully initialized yet - a consequence of a circular reference");
  32. }
  33. else {
  34. logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
  35. }
  36. }
  37. bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  38. }
  39. else {
  40. // Fail if we're already creating this bean instance:
  41. // We're assumably within a circular reference.
  42. //不执行下面if语块
  43. if (isPrototypeCurrentlyInCreation(beanName)) {
  44. throw new BeanCurrentlyInCreationException(beanName);
  45. }
  46. // Check if bean definition exists in this factory.
  47. BeanFactory parentBeanFactory = getParentBeanFactory();
  48. //parentBeanFactory为null,不执行下面if语块
  49. if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
  50. // Not found -> check parent.
  51. String nameToLookup = originalBeanName(name);
  52. if (parentBeanFactory instanceof AbstractBeanFactory) {
  53. return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
  54. nameToLookup, requiredType, args, typeCheckOnly);
  55. }
  56. else if (args != null) {
  57. // Delegation to parent with explicit args.
  58. return (T) parentBeanFactory.getBean(nameToLookup, args);
  59. }
  60. else if (requiredType != null) {
  61. // No args -> delegate to standard getBean method.
  62. return parentBeanFactory.getBean(nameToLookup, requiredType);
  63. }
  64. else {
  65. return (T) parentBeanFactory.getBean(nameToLookup);
  66. }
  67. }
  68. if (!typeCheckOnly) {
  69. markBeanAsCreated(beanName);
  70. }
  71. try {
  72. //下面方法返回Root bean: class [com.lun.interview.circular.A];
  73. //scope=singleton; abstract=false; lazyInit=false;
  74. //autowireMode=0; dependencyCheck=0; autowireCandidate=true;
  75. //primary=false; factoryBeanName=null; factoryMethodName=null;
  76. //initMethodName=null; destroyMethodName=null;
  77. //defined in class path resource [beans.xml]
  78. //重点关注scope=singleton
  79. RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
  80. checkMergedBeanDefinition(mbd, beanName, args);
  81. // Guarantee initialization of beans that the current bean depends on.
  82. String[] dependsOn = mbd.getDependsOn();
  83. //dependsOn返回null,不执行下面if语块
  84. if (dependsOn != null) {
  85. for (String dep : dependsOn) {
  86. if (isDependent(beanName, dep)) {
  87. throw new BeanCreationException(mbd.getResourceDescription(), beanName,
  88. "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
  89. }
  90. registerDependentBean(dep, beanName);
  91. try {
  92. getBean(dep);
  93. }
  94. catch (NoSuchBeanDefinitionException ex) {
  95. throw new BeanCreationException(mbd.getResourceDescription(), beanName,
  96. "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
  97. }
  98. }
  99. }
  100. // Create bean instance.
  101. //mbd.isSingleton()返回true,执行下面if语块
  102. if (mbd.isSingleton()) {
  103. sharedInstance = getSingleton(beanName, () -> {
  104. try {
  105. --------------------------->//<---------------------重点关注点是这里
  106. return createBean(beanName, mbd, args);
  107. }
  108. catch (BeansException ex) {
  109. // Explicitly remove instance from singleton cache: It might have been put there
  110. // eagerly by the creation process, to allow for circular reference resolution.
  111. // Also remove any beans that received a temporary reference to the bean.
  112. destroySingleton(beanName);
  113. throw ex;
  114. }
  115. });
  116. bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  117. }
  118. ----------------------------下面代码可略读---------
  119. ------------------
  120. else if (mbd.isPrototype()) {
  121. // It's a prototype -> create a new instance.
  122. Object prototypeInstance = null;
  123. try {
  124. beforePrototypeCreation(beanName);
  125. prototypeInstance = createBean(beanName, mbd, args);
  126. }
  127. finally {
  128. afterPrototypeCreation(beanName);
  129. }
  130. bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
  131. }
  132. else {
  133. String scopeName = mbd.getScope();
  134. if (!StringUtils.hasLength(scopeName)) {
  135. throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
  136. }
  137. Scope scope = this.scopes.get(scopeName);
  138. if (scope == null) {
  139. throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
  140. }
  141. try {
  142. Object scopedInstance = scope.get(beanName, () -> {
  143. beforePrototypeCreation(beanName);
  144. try {
  145. return createBean(beanName, mbd, args);
  146. }
  147. finally {
  148. afterPrototypeCreation(beanName);
  149. }
  150. });
  151. bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
  152. }
  153. catch (IllegalStateException ex) {
  154. throw new BeanCreationException(beanName,
  155. "Scope '" + scopeName + "' is not active for the current thread; consider " +
  156. "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
  157. ex);
  158. }
  159. }
  160. }
  161. catch (BeansException ex) {
  162. cleanupAfterBeanCreationFailure(beanName);
  163. throw ex;
  164. }
  165. }
  166. // Check if required type matches the type of the actual bean instance.
  167. if (requiredType != null && !requiredType.isInstance(bean)) {
  168. try {
  169. T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
  170. if (convertedBean == null) {
  171. throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
  172. }
  173. return convertedBean;
  174. }
  175. catch (TypeMismatchException ex) {
  176. if (logger.isTraceEnabled()) {
  177. logger.trace("Failed to convert bean '" + name + "' to required type '" +
  178. ClassUtils.getQualifiedName(requiredType) + "'", ex);
  179. }
  180. throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
  181. }
  182. }
  183. return (T) bean;
  184. }
  185. }

AbstractBeanFactory继承了FactoryBeanRegistrySupport

  1. package org.springframework.beans.factory.support;
  2. ...
  3. public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {
  4. ...
  5. }

FactoryBeanRegistrySupport继承了DefaultSingletonBeanRegistry,DefaultSingletonBeanRegistry也就是前文讨论三级缓存所在的类。

  1. package org.springframework.beans.factory.support;
  2. ...
  3. public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
  4. ...
  5. /** Cache of singleton objects: bean name to bean instance. */
  6. private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  7. /** Cache of singleton factories: bean name to ObjectFactory. */
  8. private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
  9. /** Cache of early singleton objects: bean name to bean instance. */
  10. private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
  11. @Override
  12. @Nullable
  13. public Object getSingleton(String beanName) {
  14. return getSingleton(beanName, true);
  15. }
  16. /**
  17. * Return the (raw) singleton object registered under the given name.
  18. * <p>Checks already instantiated singletons and also allows for an early
  19. * reference to a currently created singleton (resolving a circular reference).
  20. */
  21. @Nullable
  22. protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  23. //beanName为a,查找缓存,显然返回null
  24. Object singletonObject = this.singletonObjects.get(beanName);
  25. //singletonObject为null,下面if语块不执行
  26. if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
  27. synchronized (this.singletonObjects) {
  28. singletonObject = this.earlySingletonObjects.get(beanName);
  29. if (singletonObject == null && allowEarlyReference) {
  30. ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
  31. if (singletonFactory != null) {
  32. singletonObject = singletonFactory.getObject();
  33. this.earlySingletonObjects.put(beanName, singletonObject);
  34. this.singletonFactories.remove(beanName);
  35. }
  36. }
  37. }
  38. }
  39. //返回null
  40. return singletonObject;
  41. }
  42. }

本节太晕了,画个图理解
Spring循环依赖debug源码图

再次A / B两对象在三级缓存中的迁移说明

  1. A创建过程中需要B,于是A将自己放到三级缓里面,去实例化B。
  2. B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A。
  3. B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态),然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。

    6. spring循环依赖小总结

    Spring创建 bean主要分为两个步骤,创建原始bean对象,接着去填充对象属性和初始化

每次创建 bean之前,我们都会从缓存中查下有没有该bean,因为是单例,只能有一个

当我们创建 beanA的原始对象后,并把它放到三级缓存中,接下来就该填充对象属性了,这时候发现依赖了beanB,接着就又去创建beanB,同样的流程,创建完beanB填充属性时又发现它依赖了beanA又是同样的流程,
不同的是:这时候可以在三级缓存中查到刚放进去的原始对象beanA.所以不需要继续创建,用它注入 beanB,完成 beanB的创建

既然 beanB创建好了,所以 beanA就可以完成填充属性的步骤了,接着执行剩下的逻辑,闭环完成
图片.png
Spring解决循环依赖依靠的是Bean的”中间态”这个概念,而这个中间态指的是已经实例化但还没初始化的状态—>半成债。实例化的过程又是通过构造器创建的,如果A还没创建好出来怎么可能提前曝光,所以构造器的循环依赖无法解决。”对
Spring为了解决单例的循坏依赖问题,使用了三级缓存:

其中一级缓存为单例池(singletonObjects)。
二级缓存为提前曝光对象(earlySingletonObjects)。
三级级存为提前曝光对象工厂(singletonFactories) 。

假设A、B循环引用,实例化A的时候就将其放入三级缓存中,接着填充属性的时候,发现依赖了B,同样的流程也是实例化后放入三级缓存,接着去填充属性时又发现自己依赖A,这时候从缓存中查找到早期暴露的A,没有AOP代理的话,直接将A的原始对象注入B,完成B的初始化后,进行属性填充和初始化,这时候B完成后,就去完成剩下的A的步骤,如果有AOP代理,就进行AOP处理获取代理后的对象A,注入B,走剩下的流程。
[

](https://blog.csdn.net/u011863024/article/details/115270840)
Spring解决循环依赖过程:

  1. 调用doGetBean()方法,想要获取beanA,于是调用getSingleton()方法从缓存中查找beanA
  2. 在getSingleton()方法中,从一级缓存中查找,没有,返回null
  3. doGetBean()方法中获取到的beanA为null,于是走对应的处理逻辑,调用getSingleton()的重载方法(参数为ObjectFactory的)
  4. 在getSingleton()方法中,先将beanA_name添加到一个集合中,用于标记该bean正在创建中。然后回调匿名内部类的creatBean方法
  5. 进入AbstractAutowireCapableBeanFactory#ndoCreateBean,先反射调用构造器创建出beanA的实例,然后判断:是否为单例、是否允许提前暴露引用(对于单例一般为true)、是否正在创建中(即是否在第四步的集合中)。判断为true则将beanA添加到【三级缓存】中
  6. 对beanA进行属性填充,此时检测到beanA依赖于beanB,于是开始查找beanB
  7. 调用doGetBean()方法,和上面beanA的过程一样,到缓存中查找beanB,没有则创建,然后给beanB填充属性
  8. 此时 beanB依赖于beanA,调用getSingleton()获取beanA,依次从一级、二级、三级缓存中找,此时从三级缓存中获取到beanA的创建工厂,通过创建工厂获取到singletonObject,此时这个singletonObject指向的就是上面在doCreateBean()方法中实例化的beanA
  9. 这样beanB就获取到了beanA的依赖,于是beanB顺利完成实例化,并将beanA从三级缓存移动到二级缓存中
  10. 随后beanA继续他的属性填充工作,此时也获取到了beanB,beanA也随之完成了创建,回到getsingleton()方法中继续向下执行,将beanA从二级缓存移动到一级缓存中

参考

三级缓存 https://blog.csdn.net/m0_46741750/article/details/119579315 aop https://blog.csdn.net/u011863024/article/details/115270840
检测循环依赖 https://www.cnblogs.com/isnotnull/p/14300376.html