spring的循环依赖问题

1. Spring的AOP顺序

AOP执行顺序:

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

1.1 spring4下的aop测试案例

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

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>

	<parent>
	    <groupId>org.springframework.bootgroupId>
	    <artifactId>spring-boot-starter-parentartifactId>
	    <version>1.5.9.RELEASEversion>
	    <relativePath/> 
	parent>

	<groupId>com.lungroupId>
	<artifactId>HelloSpringBootartifactId>
	<version>1.0.0-SNAPSHOTversion>
	<packaging>jarpackaging>

	<name>HelloSpringBootname>
	<url>http://maven.apache.orgurl>

	<properties>
		<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
	properties>

	<dependencies>
		
		<dependency>
		    <groupId>ch.qos.logbackgroupId>
		    <artifactId>logback-coreartifactId>
		    <version>1.1.3version>
		dependency>
		
		<dependency>
		    <groupId>ch.qos.logbackgroupId>
		    <artifactId>logback-accessartifactId>
		    <version>1.1.3version>
		dependency>
		
		<dependency>
		    <groupId>ch.qos.logbackgroupId>
		    <artifactId>logback-classicartifactId>
		    <version>1.1.3version>
		dependency>
		
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-actuatorartifactId>
		dependency>
		
		
		
		
		<dependency>
			<groupId>org.apache.commonsgroupId>
			<artifactId>commons-pool2artifactId>
		dependency>
		
		
		<dependency>
			<groupId>redis.clientsgroupId>
			<artifactId>jedisartifactId>
			<version>3.1.0version>
		dependency>

		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-aopartifactId>
		dependency>
		
		
		<dependency>
			<groupId>org.redissongroupId>
			<artifactId>redissonartifactId>
			<version>3.13.4version>
		dependency>

		
		<dependency>
		    <groupId>org.springframework.bootgroupId>
		    <artifactId>spring-boot-devtoolsartifactId>
		    <scope>runtimescope>
		    <optional>trueoptional>
		dependency>
		
		<dependency>
		    <groupId>org.projectlombokgroupId>
		    <artifactId>lombokartifactId>
		    <optional>trueoptional>
		dependency>
		
		<dependency>
		    <groupId>org.springframework.bootgroupId>
		    <artifactId>spring-boot-starter-testartifactId><scope>testscope>
		    <exclusions>
		        <exclusion>
		            <groupId>org.junit.vintagegroupId>
		            <artifactId>junit-vintage-engineartifactId>
		        exclusion>
		    exclusions>
		dependency>

	dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.bootgroupId>
				<artifactId>spring-boot-maven-pluginartifactId>
			plugin>
		plugins>
	build>

project>

② 启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MainApplication {
	public static void main(String[] args) {
		SpringApplication.run(MainApplication.class, args);
	}
}

③ 接口CalcService

public interface CalcService {	
	public int div(int x, int y);
}

④ 接口实现类CalcServiceImpl新加@Service

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容器管理
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我是前置通知");
    }

    @After("execution(public int com.lun.interview.service.CalcServiceImpl.*(..))")
    public void afterNotify() {
        System.out.println("********@After我是后置通知");
    }

    @AfterReturning("execution(public int com.lun.interview.service.CalcServiceImpl.*(..))")
    public void afterReturningNotify() {
        System.out.println("********@AfterReturning我是返回后通知");
    }

    @AfterThrowing(" execution(public int com.lun.interview.service.CalcServiceImpl.*(..))")
    public void afterThrowingNotify() {
        System.out.println("********@AfterThrowing我是异常通知");
    }

    @Around(" execution(public int com.lun.interview.service.CalcServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object retvalue = null;
        System.out.println("我是环绕通知之前AAA");
        retvalue = proceedingJoinPoint.proceed();
        System.out.println("我是环绕通知之后BBB");
        return retvalue ;
    }
}

⑥ 测试类

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.lun.interview.service.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);
	}
}

⑦ aop 测试结果

输出结果:

Spring Verision : 4.3.13.RELEASE, Sring Boot Version : 1.5.9.RELEASE.

我是环绕通知之前AAA
********@Before我是前置通知
===>CalcServiceImpl被调用,计算结果为:5
我是环绕通知之后BBB
********@After我是后置通知
********@AfterReturning我是返回后通知

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

@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
	at com.lun.interview.service.CalcServiceImpl.div(CalcServiceImpl.java:10)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	...

1.2 spring5下的aop测试

① 修改POM

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0modelVersion>

	<parent>
	    <groupId>org.springframework.bootgroupId>
	    <artifactId>spring-boot-starter-parentartifactId>
	    <version>2.3.3.RELEASEversion>
	    
	    <relativePath/> 
	parent>

	<groupId>com.lungroupId>
	<artifactId>HelloSpringBootartifactId>
	<version>1.0.0-SNAPSHOTversion>
	<packaging>jarpackaging>

	<name>HelloSpringBootname>
	<url>http://maven.apache.orgurl>

	<properties>
		<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
	properties>

	<dependencies>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-webartifactId>
		dependency>
		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-actuatorartifactId>
		dependency>
		
		
		
		
		<dependency>
			<groupId>org.apache.commonsgroupId>
			<artifactId>commons-pool2artifactId>
		dependency>
		
		
		<dependency>
			<groupId>redis.clientsgroupId>
			<artifactId>jedisartifactId>
			<version>3.1.0version>
		dependency>

		
		<dependency>
			<groupId>org.springframework.bootgroupId>
			<artifactId>spring-boot-starter-aopartifactId>
		dependency>
		
		
		<dependency>
			<groupId>org.redissongroupId>
			<artifactId>redissonartifactId>
			<version>3.13.4version>
		dependency>

		
		<dependency>
		    <groupId>org.springframework.bootgroupId>
		    <artifactId>spring-boot-devtoolsartifactId>
		    <scope>runtimescope>
		    <optional>trueoptional>
		dependency>
		
		<dependency>
		    <groupId>org.projectlombokgroupId>
		    <artifactId>lombokartifactId>
		    <optional>trueoptional>
		dependency>
		
		<dependency>
		    <groupId>org.springframework.bootgroupId>
		    <artifactId>spring-boot-starter-testartifactId><scope>testscope>
		    <exclusions>
		        <exclusion>
		            <groupId>org.junit.vintagegroupId>
		            <artifactId>junit-vintage-engineartifactId>
		        exclusion>
		    exclusions>
		dependency>

	dependencies>
	
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.bootgroupId>
				<artifactId>spring-boot-maven-pluginartifactId>
			plugin>
		plugins>
	build>

project>

② 修改测试类

import javax.annotation.Resource;

import org.junit.jupiter.api.Test;
//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.lun.interview.service.CalcService;

@SpringBootTest
//@RunWith(SpringRunner.class)
public class AopTest {

	@Resource
	private CalcService calcService;
	
	@Test
	public void testAop5() {
		System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
				SpringVersion.getVersion(), SpringBootVersion.getVersion()));
		System.out.println();
		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

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

@SpringBootTest
public class AopTest {

	@Resource
	private CalcService calcService;

    ...
	
	@Test
	public void testAop5() {
		System.out.println(String.format("Spring Verision : %s, Sring Boot Version : %s.", //
				SpringVersion.getVersion(), SpringBootVersion.getVersion()));
		System.out.println();
		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
	at com.lun.interview.service.CalcServiceImpl.div(CalcServiceImpl.java:10)
	at com.lun.interview.service.CalcServiceImpl$$FastClassBySpringCGLIB$$355acbc4.invoke()
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)

2. spring 循环依赖

2.1 spring 循环依赖相关问题

1)spring 中的三级缓存分别是什么?三个Map有什么异同?

在 Spring 容器中,为了提高 bean 实例化的效率,采用了三级缓存机制来管理 bean 实例化过程中的缓存。通过使用三级缓存,Spring 可以实现对 bean 实例化过程中间状态的缓存,从而提高了 bean 的创建速度和程序的性能。同时,也为我们提供了更好的理解和掌握 Spring 中 bean 的创建流程和机制的方式。

在 Spring 容器中,三级缓存主要包括如下三个 Map:

  • singletonObjects一级缓存,单例池(singletonObjects)。该 Map 用于存储经过完整生命周期的单例bean 对象。当我们调用 getBean() 方法获取一个单例 bean 对象时,Spring 会首先从该 Map 中查找是否存在该bean 对象的实例,如果已经存在,则直接返回缓存中的实例对象。
  • earlySingletonObjects二级缓存,提前曝光对象(earlySingletonObjects)。该 Map缓存那些正在创建过程中的单例 bean 对象。当 Spring 创建单例 bean 的时候,会先将对象放入二级缓存中,然后再继续实例化 bean,完成初始化后再将对象从二级缓存中移动到一级缓存中。
  • singletonFactories三级缓存,提前曝光对象工厂(singletonFactories)。在该缓存中,Spring 会缓存用于创建单例 bean 的 ObjectFactory。当调用 getBean() 方法创建 bean 的时候,如果 bean 还没被创建,Spring 会先调用 factory.getObject() 方法创建一个空对象,并把它放入 singletonFactories 缓存中,以供后续使用。

这三个Map 的主要异同点如下:

  • singletonObjects 用于存储已经初始化完成的单例 bean 对象, earlySingletonObjects则用于存储正在初始化的 bean 对象,进而支持循环依赖问题的解决。singletonFactories 用于存储创建单例 bean 的Factory 对象,用于延迟初始化和解决依赖问题。
  • singletonObjects 和 earlySingletonsObjects 都是在单例 bean 创建过程中使用的缓存,其中 singletonObjects 用于存储实例化完成的单例对象,而 earlySingletonsObjects 用于存储正在创建中的bean 对象。singletonFactories 则主要用于支持在循环依赖时的创建和缓存。
  • 在操作上,singletonObjects 和 earlySingletonsObjects 存在读写操作,因此需要使用线程安全的并发Map。而 singletonFactories 只需要在创建 bean 的时候进行缓存,不会发生并发问题,因此可以直接使用 HashMap进行实现。

2)什么是循环依赖?如何检测是否存在循环依赖?实际开发中见过循环依赖的异常吗?

① 循环依赖是指两个或多个 bean 之间相互依赖,形成了一个闭环。 比如 bean A 依赖 bean B,而 bean B 也依赖 bean A,这样就形成了循环依赖。

② Spring 检测循环依赖主要通过三级缓存和构造函数注入来实现。 当 Spring 创建一个 bean 实例时,会首先检查是否存在该 bean 的二级缓存(earlySingletonObjects)中的对象,如果存在,则将其返回。否则,会从一级缓存(singletonObjects)中查找,如果也不存在,则需要创建该 bean 实例。

当创建该 bean 实例时,Spring 会先将用于创建该 bean 的 Factory 对象放入三级缓存(singletonFactories)中,并标记 bean 正在创建,然后继续创建 bean 所依赖的其他 bean 实例。当创建依赖 bean B 时,发现 bean B 也依赖 bean A,此时 Spring 需要提前返回一个 “早期” bean B 实例,以避免死循环。

在这种情况下,如果 bean B 和 bean A 都采用构造函数注入,则 Spring 会抛出 BeanCurrentlyInCreationException 异常,提示存在循环依赖。如果 bean A 使用了 setter 注入,则 Spring 会创建一个"空壳" 的 bean A 实例,并返回,以避免循环依赖问题。

总的来说,Spring 通过三级缓存和构造函数注入来检测循环依赖问题,并且在发现循环依赖时,会及时抛出异常或者创建空壳实例以避免死循环问题的出现。

③ 在 Spring 开发中,常见的循环依赖异常有两种:

  • BeanCurrentlyInCreationException:该异常表示当前 bean 正在创建之中,而容器又需要获取该 bean,导致出现死循环。通常发生在构造函数注入中,即 bean A 依赖 bean B,而 bean B 又依赖 bean A 的情况下。
  • NullPointerException:该异常表示 bean 依赖的其他 bean 尚未被初始化,并且 bean 访问了该依赖,导致出现空指针异常。通常发生在 setter 注入中,即 bean A 依赖 bean B,而 bean B 又依赖 bean A 的情况下。

这两种异常都是由于循环依赖引起的。如果 bean 之间存在循环依赖关系,则需要通过适当的配置或者设计来解决。一般来说,使用构造函数注入方式并尽量避免循环依赖是比较好的选择。也可以考虑使用 @Lazy 注解延迟加载 bean 或者使用 @DependsOn 注解显式指定 bean 的依赖顺序等方式来解决循环依赖问题。

在 Spring 中,@Lazy 注解是一种用于延迟加载 bean 的注解。当使用 @Lazy 注解标记一个 bean 时,Spring 容器将推迟实例化它,直到该 bean 第一次被请求使用时才会进行初始化。
@Lazy 注解的实现原理比较简单,其本质是在 bean 的注册过程中,将需要延迟加载的 bean 标记为“懒加载”状态。这样,当容器启动时,它将创建一个代理对象来代替实际的 bean,然后在第一次调用该 bean 时才会触发实际的 bean 创建和初始化过程。
具体来说,当使用 @Lazy 注解标注一个 bean 时,Spring 容器将会在 BeanDefinition 中设置一个 lazyInit 标志位,表示该 bean 是否需要延迟加载。在创建该 bean 的代理对象时,Spring 会将 lazyInit 标志位传递给代理对象,并在代理对象中实现对被代理对象的延迟加载,也就是在首次调用代理对象时,通过 getObject() 方法获取真正的 bean 实例,并进一步执行其生命周期方法。
在多数情况下,使用 @Lazy 注解可以提高系统的性能表现,减少应用启动时间和内存占用,特别是当有部分 bean 比较耗时或者内存占用较大时,可以利用延迟加载策略来避免过早创建对象带来的性能问题。

在 Spring 中,@DependsOn 注解用于指定 bean 之间的依赖关系,表示需要等待某个 bean 初始化完成后再初始化当前 bean。具体来说,当一个 bean 定义了 @DependsOn 注解时,Spring 容器将优先初始化该 bean 所依赖的 bean,并确保它们都已经完成了初始化后才会初始化当前 bean。
@DependsOn 注解的底层原理是在 bean 构建过程中的初始化阶段,通过判断当前 bean 是否存在依赖关系,如果存在,则先初始化依赖的 bean,然后再初始化当前 bean。所以,在使用 @DependsOn 注解时,需要注意避免形成循环依赖,否则可能导致启动失败或其他异常问题。
具体地,@DependsOn 注解的实现原理类似于 @Autowired 注解,底层都是利用 BeanFactory 和 BeanDefinition 来完成的。在容器启动时,首先会根据配置文件或注解扫描的方式创建 BeanFactory,接着创建 BeanDefinition,并在 BeanDefinition 中设置依赖关系。在加载 bean 的过程中,Spring 容器会在 BeanDefinition 中查找与当前 bean 存在依赖关系的 bean,然后先按照依赖顺序初始化它们,最后再初始化当前 bean。这样才能保证调用 bean 实例方法时,所依赖的其他 bean 实例已经完全初始化完成,从而避免出现 NPE 等问题。
总之,@DependsOn 注解的底层原理是在 bean 的注册过程中,通过依赖关系来确定 bean 的初始化顺序,并保证它们按照正确的顺序进行初始化。它和 @Lazy 注解一样,都是用来优化系统性能和解决特定场景下的问题的有效手段。

在 Spring 中,使用 @Autowired 注解来自动装配其他 bean 时,底层实现原理是通过 Java 的反射机制来实现的。
具体来说,在执行加载 bean 的过程中,Spring 容器会默认扫描包路径下的所有类和注解,并将它们注册到一个内部的 BeanDefinitionRegistry 中。当使用 @Autowired 注解时,Spring 容器会在这个注册表中查找与被注入的 bean 类型匹配的 bean,然后通过反射调用其 setter 方法或者字段来完成自动装配。
在进行自动装配时,Spring 支持多种不同的装配方式,包括构造函数注入、setter 方法注入、字段注入等。在使用 @Autowired 注解时,默认使用的是根据类型自动装配的方式,即从容器中查找与被注入的 bean 类型匹配的 bean,并将其注入到被注入的 bean 中。
除了 @Autowired 注解外,Spring 还提供了其他多个装配注解,例如 @Resource、@Inject 等,它们都是基于 Java 的反射机制实现的,但提供不同的装配方式和粒度,可以根据具体的业务需求进行选择和使用。

3)多例的情况下,循环依赖问题为什么无法解决

在 Spring 中,循环依赖主要是通过 singleton scope 的 bean 来进行解决。因为 singleton scope 的 bean 可以被缓存,所以 Spring 能够在创建 bean 的过程中检测到循环依赖,并提供一种解决方案来打破循环依赖。

然而,对于 prototype scope 的 bean,Spring 无法缓存这些对象,也无法有效地解决依赖问题。如果两个或多个 prototype scope 的 bean 存在循环依赖关系,那么 Spring 将无法得知它们之间的依赖关系,从而导致循环依赖问题不可避免。

这是因为 prototype scope 的 bean 在每次被请求时都会创建一个新的实例。如果一个 prototype scope 的 bean 依赖于另一个 prototype scope 的 bean,那么每次创建 bean 实例时都会重新创建一个新的依赖 bean 实例,从而无法建立稳定的依赖关系,导致循环依赖问题无法解决。

因此,在开发过程中,如果需要使用 prototype scope 的 bean,并且存在循环依赖关系,那么需要采用其他方式来解决该问题,例如将依赖关系转换为单向的、避免使用 prototype scope 的 bean 或者手动管理 bean 的生命周期等。


通常来说,如果问Spring容器内部如何解决循环依赖,一定是指默认的单例Bean中,属性互相引用的场景。
img

两种注入方式对循环依赖的影响,循环依赖官网说明:

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 ,就不会有循环依赖问题。

2.2 spring循环依赖验证案例

Spring容器循环依赖报错演示BeanCurrentlylnCreationException。

循环依赖现象在spring容器中注入依赖的对象,有2种情况:

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

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

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

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

@Component
public class ServiceBB{
    private ServiceAA serviceAA;
    
    public void setServiceAA(ServiceAA serviceAA){
        this.serviceAA = serviceAA;
        System.out.println("B里面设置了A");
    }
}
@Component
public class ServiceAA{
    private ServiceBB serviceBB;
    
    public void setServiceBB(ServiceBB serviceBB){
        this.serviceBB = serviceBB;
        System.out.println("A里面设置了B");
    }
}
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

2.3 spring循环依赖bug演示

① beans:A,B

public class A {
	private B b;
	
	public B getB() {
		return b;
	}

	public void setB(B b) {
		this.b = b;
        System.out.println("A call setB.");
	}
}
public class B {

	private A a;

	public A getA() {
		return a;
	}

	public void setA(A a) {
		this.a = a;
        System.out.println("B call setA.");
	}	
}

② 运行类

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ClientSpringContainer {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		A a = context.getBean("a", A.class);
		B b = context.getBean("b", B.class);
	}
}

1)bean 是单例(Singleton)模式场景

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

① beans.xml


<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.lun.interview.circular.A">
    	<property name="b" ref="b">property>
    bean>
    <bean id="b" class="com.lun.interview.circular.B">
    	<property name="a" ref="a">property>
    bean>
    
beans>

② 输出结果

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

2)bean 是原型(Prototype)模式场景

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

① beans.xml


<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.lun.interview.circular.A" scope="prototype">
    	<property name="b" ref="b">property>
    bean>
    <bean id="b" class="com.lun.interview.circular.B" scope="prototype">
    	<property name="a" ref="a">property>
    bean>
    
beans>

② 输出结果

00:01:39.904 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@6d86b085
00:01:40.062 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [beans.xml]
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?
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1697)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1442)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:342)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
	at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1115)
	at com.lun.interview.circular.ClientSpringContainer.main(ClientSpringContainer.java:10)
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?
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:113)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1697)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1442)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:342)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
	... 9 more
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?
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:268)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:330)
	... 17 more

3)重要结论

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

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

  • 第一级缓存(也叫单例池)singletonObjects:存放已经经历了完整生命周期的Bean对象。
  • 第二级缓存:earlySingletonObjects,存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完。
  • 第三级缓存:Map> 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);
 
    ...
    
}

2.4 spring循环依赖前置知识

实例化 - 内存中申请一块内存空间,如同租赁好房子,自己的家当还未搬来。

初始化属性填充 - 完成属性的各种赋值,如同装修,家具,家电进场。

3个Map和四大方法,总体相关对象

img

第一层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;

}

2.5 spring循环依赖源码

DEBUG一步一步来,scope默认为singleton

① 从运行类启航

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ClientSpringContainer {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
		A a = context.getBean("a", A.class);
		B b = context.getBean("b", B.class);
	}

}
package org.springframework.context.support;

...

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
    
    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
		this(new String[] {configLocation}, true, null);
	}
    
	public ClassPathXmlApplicationContext(
			String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
			throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
            //源于AbstractXmlApplicationContext
            //->AbstractRefreshableConfigApplicationContext
            //->AbstractRefreshableApplicationContext
            //->AbstractApplicationContext的refresh()
			refresh();
		}
	}
    
}
package org.springframework.context.support;

...

public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
    ...
}
package org.springframework.context.support;

...

public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
		implements BeanNameAware, InitializingBean {
    ...
}
package org.springframework.context.support;

...

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
    ...
}
package org.springframework.context.support;

...

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
    
    ...
        
    @Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

--------------->//<---------------------重点关注点是这里
				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}
    
   	/**
	 * Finish the initialization of this context's bean factory,
	 * initializing all remaining singleton beans.
	 */
	protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		// Register a default embedded value resolver if no bean post-processor
		// (such as a PropertyPlaceholderConfigurer bean) registered any before:
		// at this point, primarily for resolution in annotation attribute values.
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
		}

		// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
			getBean(weaverAwareName);
		}

		// Stop using the temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(null);

		// Allow for caching all bean definition metadata, not expecting further changes.
		beanFactory.freezeConfiguration();

------->//<---------------------重点关注点是这里
		// Instantiate all remaining (non-lazy-init) singletons.
		beanFactory.preInstantiateSingletons();
	}
    
}

beanFactory是ConfigurableListableBeanFactory

DefaultListableBeanFactory实现了ConfigurableListableBeanFactory接口

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
		implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    ...
    
    @Override
	public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}

		// Iterate over a copy to allow for init methods which in turn register new bean definitions.
		// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
		List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
		//根据上下文beanNames为[a, b]
        
		// Trigger initialization of all non-lazy singleton beans...
		for (String beanName : beanNames) {//遍历a,b
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
				if (isFactoryBean(beanName)) {
					Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
					if (bean instanceof FactoryBean) {
						FactoryBean<?> factory = (FactoryBean<?>) bean;
						boolean isEagerInit;
						if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
							isEagerInit = AccessController.doPrivileged(
									(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
						}
						else {
							isEagerInit = (factory instanceof SmartFactoryBean &&
									((SmartFactoryBean<?>) factory).isEagerInit());
						}
						if (isEagerInit) {
							getBean(beanName);
						}
					}
				}
				else {
------------------->//<---------------------重点关注点是这里
    				//源于AbstractAutowireCapableBeanFactory
    				//->AbstractBeanFactory的getBean()
					getBean(beanName);
				}
			}
		}

------------------------------下面可略读---------------------------
		// Trigger post-initialization callback for all applicable beans...
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}    
    
}
package org.springframework.beans.factory.support;

...

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
    ...
}
package org.springframework.beans.factory.support;

...

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
	...
    @Override
	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null, null, false);
	}
    
    
	@SuppressWarnings("unchecked")
	protected <T> T doGetBean(
			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {

        //name为a
		String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
------->//<---------------------重点关注点是这里
------->//<---------------------重点关注点是这里
------->//<---------------------重点关注点是这里
    	//源于FactoryBeanRegistrySupport
    	//->DefaultSingletonBeanRegistry的getSingleton()
    	//DefaultSingletonBeanRegistry也就是上文谈论的三级缓存所在类
    	//本章节末有getSingleton()源码
    	//最后getSingleton返回null
		Object sharedInstance = getSingleton(beanName);
        //sharedInstance为null,下面if语块不执行
		if (sharedInstance != null && args == null) {
			if (logger.isTraceEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
            //不执行下面if语块
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			BeanFactory parentBeanFactory = getParentBeanFactory();
            //parentBeanFactory为null,不执行下面if语块
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				if (parentBeanFactory instanceof AbstractBeanFactory) {
					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
							nameToLookup, requiredType, args, typeCheckOnly);
				}
				else if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else if (requiredType != null) {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
				else {
					return (T) parentBeanFactory.getBean(nameToLookup);
				}
			}

			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}

			try {
				//下面方法返回Root bean: class [com.lun.interview.circular.A]; 
                //scope=singleton; abstract=false; lazyInit=false; 
                //autowireMode=0; dependencyCheck=0; autowireCandidate=true; 
                //primary=false; factoryBeanName=null; factoryMethodName=null; 
                //initMethodName=null; destroyMethodName=null; 
                //defined in class path resource [beans.xml]
                //重点关注scope=singleton
                RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				String[] dependsOn = mbd.getDependsOn();
                //dependsOn返回null,不执行下面if语块
				if (dependsOn != null) {
					for (String dep : dependsOn) {
						if (isDependent(beanName, dep)) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
						}
						registerDependentBean(dep, beanName);
						try {
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}

				// Create bean instance.
                //mbd.isSingleton()返回true,执行下面if语块
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
--------------------------->//<---------------------重点关注点是这里
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

----------------------------下面代码可略读---------
    ------------------
                
				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
					}
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// Check if required type matches the type of the actual bean instance.
		if (requiredType != null && !requiredType.isInstance(bean)) {
			try {
				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
				if (convertedBean == null) {
					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
				}
				return convertedBean;
			}
			catch (TypeMismatchException ex) {
				if (logger.isTraceEnabled()) {
					logger.trace("Failed to convert bean '" + name + "' to required type '" +
							ClassUtils.getQualifiedName(requiredType) + "'", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}
}

AbstractBeanFactory继承了FactoryBeanRegistrySupport

package org.springframework.beans.factory.support;

...

public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanRegistry {
    ...
}

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

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);
    
    @Override
	@Nullable
	public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
	}

	/**
	 * Return the (raw) singleton object registered under the given name.
	 * 

Checks already instantiated singletons and also allows for an early * reference to a currently created singleton (resolving a circular reference). */ @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { //beanName为a,查找缓存,显然返回null Object singletonObject = this.singletonObjects.get(beanName); //singletonObject为null,下面if语块不执行 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } //返回null return singletonObject; } }

本节太晕了,画个图理解

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

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

2.6 spring循环依赖小总结

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

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

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

Spring 解决循环依赖依靠的是Bean的"中间态"这个概念,而这个中间态指的是已经实例化但还没初始化的状态—>半成债。实例化的过程又是通过构造器创建的,如果A还没创建好出来怎么可能提前曝光,所以构造器的循环依赖无法解决。

Spring为了解决单例的循坏依赖问题,使用了三级缓存:

其中一级缓存为单例池(singletonObjects)。

二级缓存为提前曝光对象(earlySingletonObjects)。

三级级存为提前曝光对象工厂(singletonFactories) 。

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

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从二级缓存移动到一级缓存中

你可能感兴趣的:(待整理,框架,#,Spring,spring,面试,java)