spring 依赖注入和循环依赖问题

导读:本篇文章讲解 spring 依赖注入和循环依赖问题,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

在公司发现项目竟然有循环依赖问题。。
哈哈哈,挺搞笑的。

所以晚上我特地整理了一下关于这方面的资料与大家分享

首先说spring的三级缓存解决了哪些循环依赖问题?

循环依赖问题在Spring中主要有三种情况:

  • 通过构造方法进行依赖注入时产生的循环依赖问题。
  • 通过 Setter注入或者Field注入 进行依赖注入且是在多例(原型)模式下产生的循环依赖问题。
  • 通过 Setter注入或者Field注入 进行依赖注入且是在单例模式下产生的循环依赖问题。

注意:在Spring中,只有【第三种方式】的循环依赖问题被解决了,其他两种方式在遇到循环依赖问题时都会产生异常。

其实也很好解释:

  • 第一种构造方法注入的情况下,在new对象的时候就会堵塞住了,其实也就是”先有鸡还是先有蛋“的历史难题。
  • 第二种setter方法 && 多例的情况下,每一次getBean()时,都会产生一个新的Bean,如此反复下去就会有无穷无尽的Bean产生了,最终就会导致OOM问题的出现。

1. What Is a Circular Dependency? 什么是循环依赖?

当一个 bean A 依赖于另一个 bean B,而 bean B 也依赖于 bean A 时,就会发生循环依赖:

Bean A → Bean B → Bean A

当然,我们可以存在更多的 bean:

Bean A → Bean B → Bean C → Bean D → Bean E → Bean A

2. What Happens in Spring 2. 会发生什么?

当 Spring 上下文加载所有 bean 时,它尝试按照 bean 完全工作所需的顺序创建 bean。

假设我们没有循环依赖,我们有这样的东西:

Bean A → Bean B → Bean C

创建的顺序应该是:

Spring 将创建 bean C,然后创建 bean B (并将 bean C 注入其中) ,然后创建 bean A (并将 bean B 注入其中)。

但是使用循环依赖关系,Spring 无法决定首先创建哪个 bean,因为它们彼此依赖。在这些情况下,Spring 将在加载上下文时引发 BeanCurrentlyInCreationException

在 Spring 中使用构造函数注入时可能会发生这种情况。如果我们使用其他类型的注入,就不会出现这个问题,因为依赖项将在需要时注入,而不是在上下文加载时注入。

当发生依赖注入时,一些解决方案

1. 重新设计代码

当我们有一个循环依赖关系时,很可能我们有一个设计问题,并且责任没有很好地分离。我们应该尝试正确地重新设计组件,以便它们的层次结构设计良好,不需要循环依赖。

然而,有许多可能的原因,我们可能无法做一个重新设计,如遗留代码,代码已经测试,无法修改,没有足够的时间或资源进行完整的重新设计,等等。如果我们不能重新设计组件,我们可以尝试一些变通方法。

2. 使用 @Lazy 注解

这是一个简单的处理方法。

@Lazy注解会告诉 Spring 懒惰地初始化其中一个 bean。因此,它不会完全初始化 bean,而是创建一个代理将其注入到另一个 bean 中。注入的 bean 只有在第一次需要时才会被完全创建。

比如下面这样:

@Component
public class CircularDependencyA {

    private CircularDependencyB circB;

    @Autowired
    public CircularDependencyA(@Lazy CircularDependencyB circB) {
        this.circB = circB;
    }
}

3. 使用 Setter/Field 注入

说明:在 Spring 4.5 及更高的版本中,setXXX 上面的 @Autowired 注解是可以不写的。

最流行的变通方法之一,也是 Spring 文档所建议的,是使用 setter 注入。

简而言之,我们可以通过改变 bean 的连接方式来解决这个问题ーー使用 setter 注入(或字段注入)而不是构造函数注入。通过这种方式,Spring 创建 bean,但是直到需要时才注入依赖项。

比如这样:

@Component
public class CircularDependencyA {

    private CircularDependencyB circB;

    @Autowired
    public void setCircB(CircularDependencyB circB) {
        this.circB = circB;
    }

    public CircularDependencyB getCircB() {
        return circB;
    }
}
@Component
public class CircularDependencyB {

    private CircularDependencyA circA;

    private String message = "Hi!";

    @Autowired
    public void setCircA(CircularDependencyA circA) {
        this.circA = circA;
    }

    public String getMessage() {
        return message;
    }
}

说说spring 中的依赖注入

Spring 中有这么3种依赖注入的方式

  • 基于 field 注入(属性注入)
  • 基于 setter 注入
  • 基于 constructor 注入(构造器注入)

2.1field注入

spring 中与依赖注入相关的注解,即 @Resource@Inject@Autowired

他们提供了一种声明式的的方式来解决依赖关系:

比如这样:

@Autowired
ArbitraryClass arbObject;

@Autowired 注解属于org.springframework.beans.factory.annotation包。

剩下两个注解属于 Java 扩展包:javax.annotation.Resourcejavax.inject.Inject

@Resource是JSR-250定义的注解。Spring 在 CommonAnnotationBeanPostProcessor实现了对JSR-250的注解的处理,其中就包括@Resource

@Resource有2个属性name和type。在spring中name属性定义为bean的名字,type这是bean的类型。如果属性上加@Resource注解那么他的注入流程是

  • 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
  • 如果指定了name,则从上下文中查找名称匹配的bean进行装配,找不到则抛出异常。
  • 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
  • 如果既没有指定name,又没有指定type,则默认按照byName方式进行装配;如果没有匹配,按照byType进行装配。

@Autowired 只根据type进行注入,不会去匹配name。如果涉及到type无法辨别注入对象时,那需要依赖 @Qualifier@Primary 注解一起来修饰。

像这样:

public class Controller {
    @Autowired
    @Qualifier(name="AServer")    
    private AServer aSver; 
}

( @Inject 注解 和 @Autowired 注入流程相似)

如果要求所有的依赖项都由 Spring 框架处理,那么唯一的选择是 @Autowired 注释。

思考 用 @Resource@Inject 还是 @Autowired

现在用的最多的是 @Resource@Autowired

观点1:@Resource是J2EE的注解是Java自已的东西使用 @Resource可以减少代码和Spring之间的耦合。

原文:https://juejin.cn/post/7064823104584810532
@Resource是J2EE的注解是Java自已的东西使用 @Resource可以减少代码和Spring之间的耦合。

观点2:确认是基于Spring框架的项目,使用@Autowired就没有没有问题,使用@Autowired会显得更local,整体比较得体,也能提醒大家这是在用spring框架,使用@Resource可能会让人不确定这个容器是不是spring容器;

原文:https://juejin.cn/post/7023618746501562399#heading-12  评论区

@Autowired属性注入和spring框架绑定的较深,比如spring定义了@Autowired注解,
然后通过AutowiredAnnotationBeanPostProcessor实现了注解,

而@Resource是jsr约定的注解,所有的DI容器化的框架基本都需要去实现这个的注入,
比如spring对这个的实现是 CommonAnnotationBeanPostProcessor

所以如果你的项目确认是基于Spring框架的项目,使用@Autowired就没有没有问题,
使用@Autowired会显得更local,整体比较得体,也能提醒大家这是在用spring框架,
使用@Resource可能会让人不确定这个容器是不是spring容器;
还有一点大家都觉得jsr虽然是规范,但是也是约束,这种约束感可能使得他没在程序员群体流行起来

2.2setter注入

对于基于 setter 的注入,Spring是这么说的:

Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later.

基于 setter 的注入,则只应该被用于注入非必需的依赖,同时在类中应该对这个依赖提供一个合理的默认值。如果使用 setter 注入必需的依赖,那么将会有过多的 null 检查充斥在代码中。使用 setter 注入的一个优点是,这个依赖可以很方便的被改变或者重新注入

2.3构造器注入

@Controller
public class FooController {
  
  private final FooService fooService;
  
  @Autowired
  public FooController(FooService fooService) {
      this.fooService = fooService;
  }
 
}

使用构造器注入的好处:

  1. 保证依赖不可变(final关键字)
  2. 保证依赖不为空(省去了我们对其检查)
  3. 保证返回客户端(调用)的代码的时候是完全初始化的状态
  4. 避免了循环依赖 (启动报错了)
  5. 提升了代码的可复用性

看看Spring在文档里怎么说:

The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.

Spring 团队提倡使用基于构造方法的注入,因为这样一方面可以将依赖注入到一个不可变的变量中 (注:final 修饰的变量),另一方面也可以保证这些变量的值不会是 null。此外,经过构造方法完成依赖注入的组件 (注:比如各个 service),在被调用时可以保证它们都完全准备好了。与此同时,从代码质量的角度来看,一个巨大的构造方法通常代表着出现了代码异味,这个类可能承担了过多的责任

对于循环依赖问题:如果使用构造器注入,在spring项目启动的时候,就会抛出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?从而提醒你避免循环依赖,如果是field注入的话,启动的时候不会报错,在使用那个bean的时候才会报错

强制依赖就用构造器方式
可选、可变的依赖就用setter注入

小声的说如果是开发,我就喜欢用 @Resource 属性注入

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/63384.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!