Bootstrap

org.springframework.beans.factory.BeanCurrentlyInCreationException Bean当前正在创建中错误的解决方法,已解决,亲测有效,嘿嘿嘿


org.springframework.beans.factory.BeanCurrentlyInCreationException 异常通常表明 Spring 容器在尝试注入一个依赖时,发现这个依赖的 Bean 还在创建过程中,导致了一个循环依赖问题。这种异常经常与 BeanDefinitionValidationException 一起出现,特别是在处理构造器注入的循环依赖时。

问题分析

当 Spring 容器尝试实例化一个 Bean 时,它可能会发现该 Bean 依赖于另一个尚未完成的 Bean(也就是说,另一个 Bean 也在实例化过程中)。在单例 Bean 的情况下,Spring 默认允许通过字段注入来解决基于 setter 的循环依赖,但是构造器注入和原型(prototype)Bean 的循环依赖则不会被解决,因为它们需要在实例化过程中就完成依赖注入。

报错原因

报错原因主要是循环依赖,即两个或多个 Bean 相互依赖对方,并且这种依赖关系是在实例化过程中就需要的(如构造器注入)。

解决思路

  1. 识别循环依赖:通过异常堆栈跟踪和 Spring 配置文件或注解来识别哪些 Bean 之间存在循环依赖。
  2. 重构代码:尝试重构代码以消除循环依赖。这通常意味着将共享的功能提取到一个新的 Bean 中,或者改变 Bean 之间的依赖关系。
  3. 使用 setter 注入:如果可能,将构造器注入更改为基于 setter 的注入。这样,Spring 容器可以在实例化 Bean 后注入依赖项。
  4. 使用 @Lazy 注解:在某些情况下,你可以使用 @Lazy 注解来延迟依赖项的初始化,但这通常不是首选方法,因为它可能会引入其他复杂性。
  5. 考虑使用不同的作用域:如果你的 Bean 是原型(prototype)作用域的,并且存在循环依赖,那么你可能需要重新考虑它们的作用域。

解决方法

1. 重构代码以消除循环依赖

假设我们有两个相互依赖的 Bean:BeanABeanB

@Component
public class BeanA {

    private final BeanB beanB;

    @Autowired
    public BeanA(BeanB beanB) {
        this.beanB = beanB;
    }

    // ... 其他方法 ...
}

@Component
public class BeanB {

    private final BeanA beanA;

    @Autowired
    public BeanB(BeanA beanA) {
        this.beanA = beanA;
    }

    // ... 其他方法 ...
}

重构后的代码可能如下所示,提取共享功能到一个新的 Bean SharedService

@Component
public class SharedService {
    // ... 共享的方法或属性 ...
}

@Component
public class BeanA {

    private final SharedService sharedService;

    @Autowired
    public BeanA(SharedService sharedService) {
        this.sharedService = sharedService;
    }

    // ... 其他方法 ...
}

@Component
public class BeanB {

    private final SharedService sharedService;

    @Autowired
    public BeanB(SharedService sharedService) {
        this.sharedService = sharedService;
    }

    // ... 其他方法 ...
}
2. 使用 setter 注入(如果可能)

下滑查看解决方法

@Component
public class BeanA {

    private BeanB beanB;

    @Autowired
    public void setBeanB(BeanB beanB) {
        this.beanB = beanB;
    }

    // ... 其他方法 ...
}

// BeanB 的设置类似
3. 使用 @Lazy 注解(谨慎使用)
@Component
public class BeanA {

    private final BeanB beanB;

    @Autowired
    public BeanA(@Lazy BeanB beanB) {
        this.beanB = beanB;
    }

    // ... 其他方法 ...
}

// 注意:BeanB 仍然需要被重构以避免循环依赖,或者也使用 @Lazy

请注意,虽然 @Lazy 可以解决一些循环依赖问题,但它也可能导致性能下降,因为它会延迟 Bean 的初始化,并且在每次需要该 Bean 时都可能需要重新检查它是否已经被初始化。因此,应该谨慎使用它。

在重构代码时,始终确保理解你的应用程序的依赖关系,并考虑未来的可维护性和可扩展性。

4. 不同的作用域

当涉及到原型(prototype)作用域的 Bean 时,循环依赖的问题可能会更加复杂,因为原型 Bean 在每次请求时都会创建一个新的实例。如果两个或多个原型 Bean 之间存在循环依赖,并且每个 Bean 在实例化时都需要另一个 Bean 的新实例,那么这将会导致无限递归的创建过程。

在 Spring 中,原型 Bean 的循环依赖通常不是直接支持的,因为每次请求都会尝试创建新的实例,而没有一个“中心”实例可以用来解决依赖关系。解决此问题的通常策略是重新设计这些 Bean 之间的关系,或者至少更改它们的作用域。

下面是一个可能导致循环依赖问题的原型 Bean 示例:

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBeanA {

    private final PrototypeBeanB beanB;

    @Autowired
    public PrototypeBeanA(ApplicationContext context) {
        // 使用 ApplicationContext 手动获取 BeanB 的新实例,这可能导致问题
        this.beanB = context.getBean(PrototypeBeanB.class);
    }

    // ... 其他方法 ...
}

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBeanB {

    private final PrototypeBeanA beanA;

    @Autowired
    public PrototypeBeanB(ApplicationContext context) {
        // 使用 ApplicationContext 手动获取 BeanA 的新实例,这会导致循环依赖问题
        this.beanA = context.getBean(PrototypeBeanA.class);
    }

    // ... 其他方法 ...
}

上面的示例展示了如何在构造函数中使用 ApplicationContext 手动获取原型 Bean,这会导致循环依赖。当 Spring 尝试实例化 PrototypeBeanA 时,它会尝试获取一个新的 PrototypeBeanB 实例,而 PrototypeBeanB 又试图获取一个新的 PrototypeBeanA 实例,依此类推。

解决这个问题的策略可能包括:

  1. 重构代码:将共享的功能或状态提取到另一个单例 Bean 中,该 Bean 可以被原型 Bean 共享和访问。

  2. 使用工厂方法:使用工厂方法代替构造函数注入,这样可以在需要时控制 Bean 的创建过程,避免直接的循环依赖。

  3. 使用代理或事件:如果可能,可以使用代理或事件机制来解耦 Bean 之间的直接依赖关系。

  4. 避免在原型 Bean 之间进行循环依赖:重新设计你的应用程序,避免在原型 Bean 之间建立直接的循环依赖。这可能意味着改变 Bean 的职责或交互方式。

  5. 使用不同的作用域:如果可能,将其中一个或两个原型 Bean 更改为单例(singleton)或请求(request)作用域。但是,请注意,这可能会改变你的应用程序的行为和状态管理。

在大多数情况下,最佳的做法是避免在原型 Bean 之间创建循环依赖,并通过重构代码或改变 Bean 的交互方式来消除它们。

;