4.Spring【Java面试第三季】
- 前言
- 推荐
- 4.Spring
- 27_Aop的题目说明要求
- Spring的AOP顺序
- AOP常用注解
- 面试题
- 28_spring4下的aop测试案例
- 业务类
- 新建一个切面类MyAspect并为切面类新增两个注解:
- spring4+springboot1.5.9
- pom
- 测试类
- 29_spring4下的aop测试结果
- aop正常顺序+异常顺序
- 30_spring5下的aop测试
- spring5+springboot2.3.3
- pom
- 测试类
- aop正常顺序+异常顺序
- 结论
- 31_spring循环依赖题目说明
- Spring的循环依赖
- 恶心的大厂面试题
- 什么是循环依赖
- 两种注入方式对循环依赖的影响
- 32_spring循环依赖纯java代码验证案例
- spring容器循环依赖报错演示BeanCurrentlylnCreationException
- 循环依赖现象在Spring容器中注入依赖的对象,有2种情况
- ==构造器方式注入依赖==
- ==以set方式注入依赖==
- 33_spring循环依赖bug演示
- 重要code案例演示
- code-java基础编码
- spring容器
- 重要结论(spring内部通过3级缓存来解决循环依赖)
- 34_spring循环依赖debug前置知识
- ==循环依赖Debug-困难,请坚持==
- 实例化/初始化
- 3个Map和四大方法,总体相关对象
- A/B两对象在三级缓存中的迁移说明
- spring循环依赖debug源码
- 39_spring循环依赖小总结
- ==总结spring是如何解决的循环依赖?==
- 解释
- Debug的步骤--->Spring解决循环依赖过程
- 最后
前言
2023-2-3 13:51:54
以下内容源自
【尚硅谷Java大厂面试题第3季,跳槽必刷题目+必扫技术盲点(周阳主讲)-哔哩哔哩】
仅供学习交流使用
推荐
Java开发常见面试题详解(LockSupport,AQS,Spring循环依赖,Redis)
4.Spring
27_Aop的题目说明要求
Spring的AOP顺序
AOP常用注解
- @Before 前置通知:目标方法之前执行
- @After 后置通知:目标方法之后执行(始终执行)
- @AfterReturning 返回后通知:执行方法结束前执行(异常不执行)
- @AfterThrowing 异常通知:出现异常时候执行
- @Around 环绕通知:环绕目标方法执行
面试题
-
你肯定知道spring,那说说aop的全部通知顺序springboot或springboot2(spring4->spring5)对aop的执行顺序影响?
-
说说你使用AOP中碰到的坑?
28_spring4下的aop测试案例
业务类
- 接口CalcService
- 接口实现类CalcServicelmpl新加@Service
- 想在除法方法前后置入各种通知,引入切面编程
新建Maven工程,pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring4_aop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring4_aop</name>
<description>spring4_aop</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot AOP技术-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
启动类
package com.example.spring4_aop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Spring4AopApplication {
public static void main(String[] args) {
SpringApplication.run(Spring4AopApplication.class, args);
}
}
接口CalcService
package com.example.spring4_aop.spring.aop;
public interface CalcService {
public int div(int x, int y);
}
接口实现类CalcServiceImpl新加@Service
package com.example.spring4_aop.spring.aop;
import org.springframework.stereotype.Service;
@Service
public class CalcServiceImpl implements CalcService {
@Override
public int div(int x, int y) {
int result = x / y;
System.out.println("===>CalcServiceImpl被调用,计算结果为:" + result);
return result;
}
}
新建一个切面类MyAspect并为切面类新增两个注解:
- @Aspect 指定一个类为切面类
- @Component 纳入Spring容器管理
- code
package com.example.spring4_aop.spring.aop;
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.example.spring4_aop.spring.aop.CalcServiceImpl.*(..))")
public void beforeNotify() {
System.out.println("********@Before我是前置通知");
}
@After("execution(public int com.example.spring4_aop.spring.aop.CalcServiceImpl.*(..))")
public void afterNotify() {
System.out.println("********@After我是后置通知");
}
@AfterReturning("execution(public int com.example.spring4_aop.spring.aop.CalcServiceImpl.*(..))")
public void afterReturningNotify() {
System.out.println("********@AfterReturning我是返回后通知");
}
@AfterThrowing(" execution(public int com.example.spring4_aop.spring.aop.CalcServiceImpl.*(..))")
public void afterThrowingNotify() {
System.out.println("********@AfterThrowing我是异常通知");
}
@Around(" execution(public int com.example.spring4_aop.spring.aop.CalcServiceImpl.*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
Object retvalue = null;
System.out.println("我是环绕通知之前AAA");
retvalue = proceedingJoinPoint.proceed();
System.out.println("我是环绕通知之后BBB");
return retvalue ;
}
}
spring4+springboot1.5.9
pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/>
</parent>
测试类
package com.example.spring4_aop;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.SpringVersion;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.spring4_aop.spring.aop.CalcService;
@SpringBootTest
@RunWith(SpringRunner.class)
public class AopTest {
@Resource
private CalcService calcService;
@Test
public void testAop4() {
System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
SpringVersion.getVersion(), SpringBootVersion.getVersion()));
calcService.div(10, 2);
}
}
29_spring4下的aop测试结果
Spring Verision : 4.3.13.RELEASE, Sring Boot Version : 1.5.9.RELEASE.
我是环绕通知之前AAA
********@Before我是前置通知
===>CalcServiceImpl被调用,计算结果为:5
我是环绕通知之后BBB
********@After我是后置通知
********@AfterReturning我是返回后通知
修改测试类,让其抛出算术异常类:
package com.example.spring4_aop;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.SpringVersion;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.spring4_aop.spring.aop.CalcService;
@SpringBootTest
@RunWith(SpringRunner.class)
public class AopTest {
@Resource
private CalcService calcService;
@Test
public void testAop4() {
System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
SpringVersion.getVersion(), SpringBootVersion.getVersion()));
// calcService.div(10, 2);
calcService.div(10, 0);//将会抛出异常
}
}
输出结果:
Spring Verision : 4.3.13.RELEASE, Sring Boot Version : 1.5.9.RELEASE.
我是环绕通知之前AAA
********@Before我是前置通知
********@After我是后置通知
********@AfterThrowing我是异常通知
java.lang.ArithmeticException: / by zero
aop正常顺序+异常顺序
- 正常情况下:@Before前置通知----->@After后置通知----->@AfterRunning正常返回
- 异常情况下:@Before前置通知----->@After后置通知----->@AfterThrowing方法异常
30_spring5下的aop测试
spring5+springboot2.3.3
pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!-- <version>1.5.9.RELEASE</version>-->
<version>2.3.3.RELEASE</version>
<relativePath/>
</parent>
测试类
package com.example.spring4_aop;
import javax.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.SpringVersion;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.spring4_aop.spring.aop.CalcService;
@SpringBootTest
public class AopTest {
@Resource
private CalcService calcService;
@Test
public void testAop4() {
System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
SpringVersion.getVersion(), SpringBootVersion.getVersion()));
// calcService.div(10, 2);
calcService.div(10, 0);//将会抛出异常
}
@Test
public void testAop5() {
System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
SpringVersion.getVersion(), SpringBootVersion.getVersion()));
calcService.div(10, 2);
// calcService.div(10, 0);//将会抛出异常
}
}
Spring Verision : 5.2.8.RELEASE, Sring Boot Version : 2.3.3.RELEASE.
我是环绕通知之前AAA
********@Before我是前置通知
===>CalcServiceImpl被调用,计算结果为:5
********@AfterReturning我是返回后通知
********@After我是后置通知
我是环绕通知之后BBB
修改测试类,让其抛出算术异常类:
package com.example.spring4_aop;
import javax.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.SpringVersion;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.spring4_aop.spring.aop.CalcService;
@SpringBootTest
public class AopTest {
@Resource
private CalcService calcService;
@Test
public void testAop4() {
System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
SpringVersion.getVersion(), SpringBootVersion.getVersion()));
// calcService.div(10, 2);
calcService.div(10, 0);//将会抛出异常
}
@Test
public void testAop5() {
System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
SpringVersion.getVersion(), SpringBootVersion.getVersion()));
// calcService.div(10, 2);
calcService.div(10, 0);//将会抛出异常
}
}
输出结果
Spring Verision : 5.2.8.RELEASE, Sring Boot Version : 2.3.3.RELEASE.
我是环绕通知之前AAA
********@Before我是前置通知
********@AfterThrowing我是异常通知
********@After我是后置通知
java.lang.ArithmeticException: / by zero
aop正常顺序+异常顺序
同上结果
结论
31_spring循环依赖题目说明
Spring的循环依赖
恶心的大厂面试题
- 你解释下spring中的三级缓存?
- 三级缓存分别是什么?三个Map有什么异同?
- 什么是循环依赖?请你谈谈?看过spring源码吗?一般我们说spring容器是什么
- 如何检测是否存在循环依赖?实际开发中见过循环依赖的异常吗?
- 多例的情况下,循环依赖问题为什么无法解决?
- 。。。
什么是循环依赖
-
多个bean之间相互依赖,形成了一个闭环。比如:A依赖于B、B依赖于C、C依赖于A。
- 代码
- 代码
-
通常来说,如果问Spring容器内部如何解决循环依赖,一定是指默认的单例Bean中,属性互相引用的场景。
两种注入方式对循环依赖的影响
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#spring-core
-
循环依赖官网说明
-
结论
我们AB循环依赖问题只要A的注入方式是setter且singleton ,就不会有循环依赖问题。
32_spring循环依赖纯java代码验证案例
spring容器循环依赖报错演示BeanCurrentlylnCreationException
循环依赖现象在Spring容器中注入依赖的对象,有2种情况
构造器方式注入依赖
code
- serviceA
@Component
public class ServiceA{
private ServiceB serviceB;
public ServiceA(ServiceB serviceB){
this.serviceB = serviceB;
}
}
- serviceB
@Component
public class ServiceB{
private ServiceA serviceA;
public ServiceB(ServiceA serviceA){
this.serviceA = serviceA;
}
}
- ClientConstructor
public class ClientConstructor{
public static void main(String[] args){
new ServiceA(new ServiceB(new ServiceA()));//这会抛出编译异常
}
}
结论
- 构造器循环依赖是无法解决的你想让构造器注入支持循环依赖,是不存在的
以set方式注入依赖
code
- ServiceAA
@Component
public class ServiceAA{
private ServiceBB serviceBB;
public void setServiceBB(ServiceBB serviceBB){
this.serviceBB = serviceBB;
System.out.println("A里面设置了B");
}
}
- ServiceBB
@Component
public class ServiceBB{
private ServiceAA serviceAA;
public void setServiceAA(ServiceAA serviceAA){
this.serviceAA = serviceAA;
System.out.println("B里面设置了A");
}
}
- ClientSet
public class ClientSet{
public static void main(String[] args){
//创建serviceAA
ServiceAA a = new ServiceAA();
//创建serviceBB
ServiceBB b = new ServiceBB();
//将serviceA入到serviceB中
b.setServiceAA(a);
//将serviceB法入到serviceA中
a.setServiceBB(b);
}
}
输出结果:
B里面设置了A
A里面设置了B
33_spring循环依赖bug演示
重要code案例演示
code-java基础编码
- A
package com.example.spring4_aop.spring;
public class A {
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
public A() {
System.out.println("---A created success");
}
}
- B
package com.example.spring4_aop.spring;
public class B {
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
public B() {
System.out.println("---B created success");
}
}
- ClientCode
package com.example.spring4_aop.spring;
public class ClientCode {
public static void main(String[] args) {
A a=new A();
B b=new B();
b.setA(a);
a.setB(b);
}
}
测试结果
---A created success
---B created success
spring容器
- 默认的单例(singleton)的场景是支持循环依赖的,不报错
- 原型(Prototype)的场景是不支持循环依赖的,会报错
- 步骤
- applicationContext.xml
- 默认单例,修改为原型scope="prototype"ClientspringContainer
- 循环依赖异常
applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="a" class="com.example.spring4_aop.spring.A">
<property name="b" ref="b"></property>
</bean>
<bean id="b" class="com.example.spring4_aop.spring.B">
<property name="a" ref="a"></property>
</bean>
</beans>
ClientSpringContainer
package com.example.spring4_aop.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ClientSpringContainer {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
A a = context.getBean("a", A.class);
B b = context.getBean("b", B.class);
}
}
输出结果
15:29:16.316 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@61064425
15:29:16.552 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [applicationContext.xml]
15:29:16.607 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'a'
---A created success
15:29:16.626 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'b'
---B created success
原型(Prototype)的场景是不支持循环依赖的,会报错
applicationContext.xml
<bean id="a" class="com.example.spring4_aop.spring.A" scope="prototype">
<property name="b" ref="b"></property>
</bean>
<bean id="b" class="com.example.spring4_aop.spring.B" scope="prototype">
<property name="a" ref="a"></property>
</bean>
输出结果
15:33:03.481 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@61064425
15:33:03.669 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [applicationContext.xml]
---A created success
---B created success
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'a' defined in class path resource [applicationContext.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 [applicationContext.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?
Exception in thread "main" org.springframework.beans.factory.BeanCreationException:Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
重要结论(spring内部通过3级缓存来解决循环依赖)
DefaultSingletonBeanRegistry
只有单例的bean会通过三级缓存提前暴露来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。
第一级缓存(也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象。
第二级缓存:earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完。)
第三级缓存:Map<String, ObjectFactory<?>> singletonFactories,存放可以生成Bean的工厂。
package org.springframework.beans.factory.support;
...
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
...
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
...
}
34_spring循环依赖debug前置知识
循环依赖Debug-困难,请坚持
实例化/初始化
-
实例化
- 内存中申请一块内存空间
- 租赁好房子,自己的家当还未搬来。
-
初始化属性填充
- 完成属性的各种赋值
- 装修,家具,家电进场。
3个Map和四大方法,总体相关对象
第一层singletonObjects存放的是已经初始化好了的Bean,
第二层earlySingletonObjects存放的是实例化了,但是未初始化的Bean,
第三层singletonFactories存放的是FactoryBean。假如A类实现了FactoryBean,那么依赖注入的时候不是A类,而是A类产生的Bean
package org.springframework.beans.factory.support;
...
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
...
/**
单例对象的缓存:bean名称—bean实例,即:所谓的单例池。
表示已经经历了完整生命周期的Bean对象
第一级缓存
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
早期的单例对象的高速缓存: bean名称—bean实例。
表示 Bean的生命周期还没走完(Bean的属性还未填充)就把这个 Bean存入该缓存中也就是实例化但未初始化的 bean放入该缓存里
第二级缓存
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/**
单例工厂的高速缓存:bean名称—ObjectFactory
表示存放生成 bean的工厂
第三级缓存
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
...
}
A/B两对象在三级缓存中的迁移说明
1.A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B。
2.B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A。
3.B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态),然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后完成创建,并将A自己放到一级缓存里面。
@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
spring循环依赖debug源码
跳转
spring循环依赖debug源码
39_spring循环依赖小总结
总结spring是如何解决的循环依赖?
解释
Spring创建 bean主要分为两个步骤,创建原始bean对象,接着去填充对象属性和初始化
每次创建 bean之前,我们都会从缓存中查下有没有该bean,因为是单例,只能有一个
当我们创建 beanA的原始对象后,并把它放到三级缓存中,接下来就该填充对象属性了,这时候发现依赖了beanB,接着就又去创建beanB,同样的流程,创建完beanB填充属性时又发现它依赖了beanA又是同样的流程,
不同的是:
这时候可以在三级缓存中查到刚放进去的原始对象beanA.所以不需要继续创建,用它注入 beanB,完成 beanB的创建
既然 beanB创建好了,所以 beanA就可以完成填充属性的步骤了,接着执行剩下的逻辑,闭环完成
Spring解决循环依赖依靠的是Bean的"中间态"这个概念,而这个中间态指的是已经实例化但还没初始化的状态—>半成品。
实例化的过程又是通过构造器创建的,如果A还没创建好出来怎么可能提前曝光,所以构造器的循环依赖无法解决。”对
Spring为了解决单例的循坏依赖问题,使用了三级缓存:
其中一级缓存为单例池(singletonObjects)。
二级缓存为提前曝光对象(earlySingletonObjects)。
三级级存为提前曝光对象工厂(singletonFactories) 。
假设A、B循环引用,实例化A的时候就将其放入三级缓存中,接着填充属性的时候,发现依赖了B,同样的流程也是实例化后放入三级缓存,接着去填充属性时又发现自己依赖A,这时候从缓存中查找到早期暴露的A,没有AOP代理的话,直接将A的原始对象注入B,完成B的初始化后,进行属性填充和初始化,这时候B完成后,就去完成剩下的A的步骤,如果有AOP代理,就进行AOP处理获取代理后的对象A,注入B,走剩下的流程。
Debug的步骤—>Spring解决循环依赖过程
- 调用doGetBean()方法,想要获取beanA,于是调用getSingleton()方法从缓存中查找beanA
- 在getSingleton()方法中,从一级缓存中查找,没有,返回null
- doGetBean()方法中获取到的beanA为null,于是走对应的处理逻辑,调用getSingleton()的重载方法(参数为ObjectFactory的)
- 在getSingleton()方法中,先将beanA_name添加到一个集合中,用于标记该bean正在创建中。然后回调匿名内部类的creatBean方法
- 进入AbstractAutowireCapableBeanFactory#ndoCreateBean,先反射调用构造器创建出beanA的实例,然后判断:是否为单例、是否允许提前暴露引用(对于单例一般为true)、是否正在创建中(即是否在第四步的集合中)。判断为true则将beanA添加到【三级缓存】中
- 对beanA进行属性填充,此时检测到beanA依赖于beanB,于是开始查找beanB
- 调用doGetBean()方法,和上面beanA的过程一样,到缓存中查找beanB,没有则创建,然后给beanB填充属性
- 此时 beanB依赖于beanA,调用getSingleton()获取beanA,依次从一级、二级、三级缓存中找,此时从三级缓存中获取到beanA的创建工厂,通过创建工厂获取到singletonObject,此时这个singletonObject指向的就是上面在doCreateBean()方法中实例化的beanA
- 这样beanB就获取到了beanA的依赖,于是beanB顺利完成实例化,并将beanA从三级缓存移动到二级缓存中
- 随后beanA继续他的属性填充工作,此时也获取到了beanB,beanA也随之完成了创建,回到getsingleton()方法中继续向下执行,将beanA从二级缓存移动到一级缓存中
最后
2023-2-3 21:23:12
这篇博客能写好的原因是:站在巨人的肩膀上
这篇博客要写好的目的是:做别人的肩膀
开源:为爱发电
学习:为我而行