在这篇文章中,我们将学习 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"); } } |