深度剖析Spring循环依赖(实战Bug)

发布时间:2024年01月20日

前言

通过实战更好的回馈问题,意识更加深刻

起因是我出现如下问题之后,才意识到中了Spring的循环依赖了!

1. 问题所示

在执行项目的时候,出现如下问题,问题如下所示:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'tyreRepareOrderController' defined in file [F:\java_project\xx\xx-service\xx-equipment\target\classes\org\xx\equipment\controller\TyreRepareOrderController.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'tyreRepareOrderServiceImpl': Bean with name 'tyreRepareOrderServiceImpl' has been injected into other beans [tyreRepareOrderServiceImpl] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:769)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:218)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1338)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1185)

截图如下所示:

在这里插入图片描述

2. 原理分析

这是Spring循环依赖的经典问题了

原本我是ServiceImpl类中实现了Service中的方法!
之后又在ServiceImpl类 通过 注入 Service方法来操作,说的普通一点就是死锁了!

解决方案就是要么使用Spring中的三级缓存(这是面试的经典题)
或者使用this.super直接使用,从而不用再次注入,而又能调用其方法!

3. 基本知识

Spring循环依赖是指两个或多个Bean彼此依赖,形成一个循环引用的情况。这种情况可能导致应用程序无法正常启动或运行,因此需要特殊处理。

在Spring中,循环依赖的处理是通过使用三级缓存解决的。三级缓存是指Spring容器中的三个缓存区域,分别是singletonObjects、earlySingletonObjects和singletonFactories。在处理循环依赖时,Spring会通过提前暴露半成品Bean的方式来解决。

具体来说,当一个Bean被创建时,Spring会将其提前暴露给singletonFactories缓存,即使它还没有完全初始化。这样,其他Bean在需要引用这个Bean时,可以通过singletonFactories获取到它的引用,而不是等待其完全初始化。当Bean初始化完成后,会将其放入singletonObjects缓存,表示完全初始化的Bean。

这样,循环依赖中的Bean就能够在初始化过程中相互引用,而不会导致死循环或空指针异常。

需要注意的是,Spring的循环依赖处理机制对于单例(singleton)的Bean是有效的,对于其他作用域的Bean(如prototype)可能会有不同的处理方式。

下面是一个简单的Java代码示例,演示了Spring中的循环依赖情况:

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

@Component
public class A {
    @Autowired
    private B b;

    // Other code...
}

@Component
public class B {
    @Autowired
    private A a;

    // Other code...
}

在上述例子中,类A依赖类B,而类B又依赖类A,形成了循环依赖。

Spring会通过三级缓存解决这种循环依赖。当一个Bean被创建时,它会被提前暴露给singletonFactories缓存,以便其他Bean可以引用它。下面是如何解除循环依赖的代码解释:

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

@Component
public class A {
    @Autowired
    @Lazy
    private B b;

    // Other code...
}

@Component
public class B {
    @Autowired
    @Lazy
    private A a;

    // Other code...
}

在解除循环依赖时,可以使用@Lazy注解标记依赖关系,表示在需要时才进行实际的Bean初始化。

这样,当Bean A和Bean B相互引用时,它们的初始化将被延迟到第一次实际使用时。这有助于打破循环依赖,因为Bean的初始化被推迟到真正需要的时候。

4. @Lazy注解

@Lazy注解是Spring框架提供的一种机制,用于延迟加载(Lazy Initialization)Bean。

当一个Bean被标记为@Lazy时,它的初始化将会被推迟到第一次被实际使用的时候,而不是在容器启动时立即初始化。

延迟加载的一些补充:

优点作用范围使用场景
1.性能优化: 避免在应用启动时就初始化所有Bean,节省了启动时间和资源。

2.解决循环依赖: 通过延迟加载,可以解决某些情况下的循环依赖问题,如前面提到的循环依赖的处理。
@Lazy注解可以用于标记在字段、方法、构造函数或配置类(@Configuration)的@Bean方法上。适用于那些在启动时不一定会被立即使用的Bean,特别是当Bean的初始化过程较为耗时或涉及到一些外部资源。

与单例(Singleton)的关系: 默认情况下,Spring容器中的Bean是单例的。
使用@Lazy注解可以将单例Bean的初始化延迟到第一次使用时。

XML配置中的等效方式: 在XML配置中,可以使用lazy-init="true"属性来实现与@Lazy注解相同的效果。

<bean id="myBean" class="com.example.MyBean" lazy-init="true">
    <!-- Bean configuration -->
</bean>

总体而言,@Lazy注解是Spring框架提供的一个有用的特性,可用于优化应用程序的启动性能,尤其是在处理大型应用或有复杂依赖关系的情况下。

文章来源:https://blog.csdn.net/weixin_47872288/article/details/135662741
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。