Spring Boot 依赖注入的六种方式


在这篇文章中,我们将学习 Spring Boot 中的六种依赖注入方式。我们将用一个简单的例子来介绍每种方法并进行比较。

在Spring中的依赖注入可以通过以下方式实现:

  • 构造函数注入
  • Setters方法注入
  • 字段注入

现在,每种方法都可以用两种方式执行:

  • 使用像@Autowired这样的Java注解,让Spring扫描你代码库中的组件
  • 在你的spring config XML文件中定义每个Bean。

先决条件
由于这篇文章的目的是了解不同的注入方法,因此我们的项目设置很简单。

Laptop.java类代表笔记本电脑。现在,笔记本电脑具有不同的属性,例如Display, HardDisk, Battery。现在,让我们只考虑HardDisk.java。

public class Laptop {
    private HardDisk hardDisk;

    public Laptop(HardDisk hardDisk){
        this.hardDisk = hardDisk;
    }
    public void saveData(){
        hardDisk.saveData();
    }
}

HardDisk.java是一个由两个HardDisk实现实现的接口:SanDiskHD和HitachiHD。

public interface HardDisk {
    public void saveData();
}


public class HitachiHD implements HardDisk {
    public void saveData(){
        System.out.println("HitachiHD: saveData");
    }
}

public class SanDiskHD implements HardDisk {
    public void saveData(){
        System.out.println("SanDiskHD: saveData");
    }
}

依赖注入的意义在于在我们的代码设计中遵循松耦合原则

现在我们将讨论不同的HardDisk实现方式(即SanDiskHD和HitachiHD)可以被注入到Laptop。

使用注解的注入
根据我的经验,这种方法被广泛用于大型项目。这也是我使用最多的一种。
Spring会扫描你的Java代码中的注释,并自动在spring容器中注册Bean。

为了通过注解实现注入,你应该:

1、在你的项目中的spring配置文件中启用组件扫描:

 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="<http://www.springframework.org/schema/beans>"
        xmlns:xsi="<http://www.w3.org/2001/XMLSchema-instance>"
        xmlns:context="<http://www.springframework.org/schema/context>"
        xsi:schemaLocation="<http://www.springframework.org/schema/beans>
     <http://www.springframework.org/schema/beans/spring-beans.xsd>
     <http://www.springframework.org/schema/context>
     <http://www.springframework.org/schema/context/spring-context.xsd>">

 <!--    Enable component scan-->
     <context:component-scan base-package="com.learningspring.demo.src" />
 </beans>

2、用@Component注解来标记类。你也可以选择提供一个bean id:

 @Component("hardDisk")
 public class HitachiHD implements HardDisk {
     public void saveData(){
         System.out.println("HitachiHD: saveData");
     }
 }

3、使用Bean ID获取Bean。

 public static void main(String[] args) {
         ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
         HardDisk hd = ctx.getBean("hardDisk", HardDisk.class);
         hd.saveData();
         SpringApplication.run(DemoApplication.class, args);
 }

4、运行结果:

 20:13:24.494 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'hardDisk'
 HitachiHD: saveData


使用注解和Autowiring进行注入

1、什么是Autowiring?
Spring可以通过匹配类型(类或接口)自动找到Bean并自动注入。

因此,如果你想在Laptop中注入一个HardDisk的实现,你可以用@Autowired来注释Laptop类的HardDisk属性。

这将告诉spring扫描你代码中的所有组件,找到一个与HardDisk类型相匹配的bean,并将其注入到Laptop中。

例如:如果你用@Component注释HitachiHD,它将把HitachiHD注入到Laptop。

现在,有三种Autowiring自动连接Bean的方法:

  • 字段注入
  • 构造函数注入
  • 设置器setters注入

字段注入
用@Component来注解你想注入到Laptop的HardDisk的实现。

 @Component
 public class HitachiHD implements HardDisk {
     public void saveData(){
         System.out.println("HitachiHD: saveData");
     }
 }

通过用@Autowired注解HardDisk属性,将HardDisk Bean注入到Laptop。

 @Component
 public class Laptop {
     @Autowired
     private HardDisk hardDisk;

     public void saveData(){
         hardDisk.saveData();
     }
 }

为了测试这个自动连接,让我们获得Laptop Bean并调用它的saveData()来看看结果:

 public static void main(String[] args) {
         ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
         Laptop l = ctx.getBean("laptop", Laptop.class);
         l.saveData();
         SpringApplication.run(DemoApplication.class, args);
     }


缺点:

  • 比基于构造器或基于setter器的注入成本高。
  • 促使许多依赖关系被注入到一个类中,这将导致设计问题。


构造函数注入
有两种模式:
1、用@Autowired注解整个构造函数。
 

@Component
 public class Laptop {
     private HardDisk hardDisk;

     @Autowired
     public Laptop(HardDisk hardDisk) {
         this.hardDisk = hardDisk;
     }

     public void saveData(){
         hardDisk.saveData();
     }
 }

2、 在构造函数中只用@Autowired来注解特定的属性:

 @Component
 public class Laptop {
     private HardDisk hardDisk;

     public Laptop(@Autowired HardDisk hardDisk) {
         this.hardDisk = hardDisk;
     }

     public void saveData(){
         hardDisk.saveData();
     }
 }

Setter注入
在这里,你用@Autowired来注解相关属性的setter方法。

@Component
public class Laptop {
    private HardDisk hardDisk;

    @Autowired
    public void setHardDisk(HardDisk hardDisk) {
        this.hardDisk = hardDisk;
    }

    public void saveData(){
        hardDisk.saveData();
    }
}


你应该使用什么样的依赖注入?
一个好的答案可能是--遵循你正在工作的代码库中已经遵循的模式。

如果有多个用@Component注释的实现怎么办?
因为按类型自动布线可能会导致多个候选者,所以通常有必要对选择过程进行更多的控制。实现这一目标的方法之一是使用Spring的@Primary注解。

@Primary表示,当多个Bean是自动连接到一个单值依赖关系的候选者时,应该优先考虑一个特定的Bean。如果在候选者中正好有一个主要Bean存在,它就会成为自动连接的值。

如果你想总是自动连接HitachiHD实现为HardDisk,你可以用@Primary来注解它。

@Primary
@Component
public class HitachiHD implements HardDisk {
    public void saveData(){
        System.out.println("HitachiHD: saveData");
    }
}