spring的循环依赖是什么,设计的时候怎么避免,而spring又是怎么解决的?

Posted 栗子~~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring的循环依赖是什么,设计的时候怎么避免,而spring又是怎么解决的?相关的知识,希望对你有一定的参考价值。

文章目录

前言

  如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。
  而且听说点赞的人每天的运气都不会太差,实在白嫖的话,那欢迎常来啊!!!


spring的循环依赖是什么,设计的时候怎么避免,而spring又是怎么解决的?

01 spring是如何解决spring依赖的?

站在spring整个体系的而言,spring 的Bean 它是由一个BeanDefinition来的,就是我们在spring当中有一个叫建模的一个类BeanDefinition,
spring的bean它有一系列比较复杂的生命周期,

1、首先spring容器启动,启动完之后它会去做一个简单的扫描;
2、根据扫描的内容和spring.factories里面的内容,然后把它变成BeanDefinition,并存到BeanDefinitionMap中;
3、然后再去对BeanDefinitionMap做遍历,之后做验证,比如验证是否为单例,是否原型,是否为懒加载等等;
4、验证完之后容器往下执行,获取一遍当前实例化这个类有没有存在单例池当中,有没有提前暴露,提前暴露的话,那么spring的Bean就会开始创建我们这个bean;
5、创建bean,第一步通过推断构造方法这一个过程,如果有多个构造方法,那么推断构造方法,即获取最佳的构造方法;
6、通过反射实例化一个java对象,根据这个java对象,对这个Bean做一些初始化工作,
比如说对这个bean是否要去进行一些BeanDefinition合并,咱们这个容器是否支持循环依赖,如果支持循环依赖的话,它会提前暴露一个当前java对象(半成品)所对应的一个ObjectFactory工厂类,把这个工厂类存到一个 二级缓存中,存在一个map当中
7、暴露完成之后,它会去做这个属性填充,即自动注入;
8、接下来执行各种aware方法回调,比如BeanNameAware、BeanFactoryAware等等;
9、接下来做生命周期初始化的回调,比如我们加上了@PostConstrout或者xml这种初始化生命周期的回调方法

比如:
调用BeanPostProcessor的初始化的前方法【后置处理器的前处理】.
调用初始化方法
调用BeanPostProcessor的初始化的后方法,在这里进行AOP(动态代理的创建),
如果没有代理进行一些事件发布,这里做的事情比较多,大概就是完成代理,生成事件等等之类的。
10、剩下的就是放在单例池当中;

在这其中spring如何解决循环依赖,它首先对X做一些基本的验证,验证完看一下我们有没有提前暴露(X对应的ObjectFactory工厂类),一直往下走,直到把X实例出来之后走到当前容器是否需要支持循环依赖,即将X对应的ObjectFactory工厂类提前暴露,当前容器默认的情况下都需要提前暴露,X就会提前暴露,注意的是这个时候暴露的并不是一个单纯的X,它暴露的是由X生成的,创建出来的一个ObjectFactory工厂类对象,之后往下执行,当执行到对X的属性填充时,发现X里面的Y的时候,就会填充Y,填充时发现Y并没有存在我们这个spring容器中,就会走Y的生命周期,同样的从BeanDefinitionMap当中把Y拿出来,过程跟X的过程一样,只到属性填充Y这一步,在对Y里面的X进行属性填充时,发现X也并不在我们这个spring容器中,因为这个流程是在X走到一半的时候走到这里,因此X并不在我们这个单例池中,然后又会走X的生命周期流程,又重复了一遍,在判断X是否被提前暴露这个环节时,发现X已经提前暴露了,所以它就能拿到我们这个X对应的ObjectFactory工厂类对象

02 问什么我们的spring循环依赖只支持单例?

如果spring 循环依赖不支持单例,那么我们第一遍我们X压根就不会走生命周期流程,因为我们单例的话,它会在spring容器初始化的时候走我们的Bean生命周期流程,如果我们是原型的话,它一开始不会走的,只有用到的时候才会走spring bean 生命周期,所以不会出现循环依赖这种情况。

03 发生循环依赖的错误代码

当一个bean A依赖于另一个bean B,并且bean B也依赖于bean A时,就会发生这种情况:
Bean A → Bean B → Bean A
因为当Spring上下文加载所有bean时,当具有循环依赖关系时,Spring无法决定应首先创建哪个bean,因为它们相互依赖。 这种情况下,Spring在加载上下文时将引发BeanCurrentlyInCreationException异常。
下面是两个相互依赖的bean例子:

@Component
public class TestA 
    private TestB testB;
    @Autowired
    public TestA(TestB testB)
        this.testB = testB;
    

@Component
public class TestB 
    private TestA testA;
    @Autowired
    public TestB(TestA testA)
        this.testA = testA;
    

测试:
制作一个TestConfig类,指定要扫描组件的基本程序包,并写一个单元测试来进行测试启动:

@Configuration
@ComponentScan(basePackages =  "com.example.demo.test" )
public class TestConfig 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes =  TestConfig.class )
public class TestConfigTest 
    @Test
    public void test_testConfig_1_0()
    

结果

04 推荐下面几种方式解决循环依赖问题

1、重新设计;
2、 @PostConstruct:
打破周期的另一种方法是在一个bean上使用@Autowired注入依赖项,然后使用带有@PostConstruct注释的方法设置另一个依赖项:

@Component
public class TestA 
    @Autowired
    private TestB testB;


    @PostConstruct
    public void init()
        testB.setTestA(this);
    
    public TestB getTestB()
        return this.testB;
    

@Component
public class TestB 
    private TestA testA;


    public void setTestA(TestA testA)
        this.testA = testA;
    
    public String test()
        return "1";
    

@Configuration
@ComponentScan(basePackages =  "com.example.demo.test" )
public class TestConfig 

测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes =  TestConfig.class )
public class TestConfigTest 
    @Autowired
    ApplicationContext context;
    @Bean
    public TestA testA() 
        return new TestA();
    
    @Bean
    public TestB testB() 
        return new TestB();
    
    @Test
    public void test_testConfig_1_0()
        TestA testA = context.getBean(TestA.class);
        Assert.assertEquals("1",testA.getTestB().test());
    

效果:

以上是关于spring的循环依赖是什么,设计的时候怎么避免,而spring又是怎么解决的?的主要内容,如果未能解决你的问题,请参考以下文章

Spring系列五:Spring怎么解决循环依赖

spring-循环依赖

spring如何解决循环依赖

Spring如何解决循环依赖?

Spring处理循环依赖只使用二级缓存可以吗?

Spring如何通过三级缓存解决循环依赖