引言
大家在SpringBoot项目开发中进行依赖注入时,是不是都喜欢用字段注入呢?即使用@Autowired
,@Resource
。没错,这种方式最简单与简洁,但是为什么在面试中,面试官问起你有关依赖注入的问题时,最好不要回答用字段注入呢?而是回答用构造器注入,接下来让我们一起来探讨一下常见的三种依赖注入方式。
字段注入(Field Injection)😵
字段注入(Field Injection)是Spring框架中依赖注入的一种方式,它允许开发者直接在类的字段上使用@Autowired
或者@Resource
注解来自动装配依赖。
下面通过一个简单例子来示例怎么使用字段注入:
java
@Service
public class MyService {
@Autowired
private Dependency dependency;
public void performAction() {
dependency.doSomething();
}
}
当这个类上面加了注解@Service,Spring就会将其当成一个Bean实例化到容器中方便取用,这样就可以避免多次创建或者销毁同一个类对象,方便复用,提高资源利用率。在这个例子中,MyService
类有一个 Dependency
类型的依赖,通过在字段上添加 @Autowired
注解,Spring 会自动在容器中寻找Bean,将合适的 Dependency
Bean 注入到这个字段中。
@Autowired
和@Resource
的区别
简单来说,@Autowired
默认时通过类型匹配来进行依赖注入,而@Resource
默认是通过基于名称来进行注入(是指bean在容器里面的名称🥹),所以你在使用@Resource
时,最好清楚指定bean在容器中的名称哦。
@Resource
只能用于字段或 setter 方法上,不能用在构造器上。- 如果你没有指定
name
属性,Spring 会尝试使用字段名或 setter 方法的属性名作为 Bean 名称进行匹配。 - 当找不到名称匹配的 Bean 时,
@Resource
不会回退到按类型匹配
设值方法注入 (Setter Injection)🤤
Setter注入通过类的setter方法来为类提供所需的依赖。这种方式与构造器注入相比,具有一定的灵活性,特别适合于那些依赖项可选或者希望在对象创建之后能够修改依赖的情况。
Tips: Setter注入通常遵循JavaBean规范,以set
开头,后跟属性名,并且只有一个参数。
java
@Service
public class MyService {
private Dependency dependency;
// Setter注入
@Autowired
public void setDependency(Dependency dependency) {
this.dependency = dependency;
}
public void performAction() {
dependency.doSomething();
}
}
在这个例子中,MyService
类有一个 Dependency
类型的依赖,通过带有 @Autowired
注解的 setter 方法 setDependency
来注入这个依赖
- 灵活性:允许在对象创建之后更改依赖关系,这对于一些需要动态改变依赖的情况非常有用。
- 可选依赖 :如果某个依赖不是必须的,那么你可以不使用
@Autowired
注解,而是直接定义一个普通的 setter 方法,这样即使没有配置相应的 Bean,应用也不会出错
请注意: 即使Spring支持setter注入可以帮助解决某些类型的循环依赖问题,但应尽量避免设计上出现循环依赖的情况😵
构造器注入 (Constructor Injection)🤩
构造器注入 是 Spring 中最推荐的依赖注入方式之一。它通过类的构造函数(构造器)来注入依赖对象。Spring 容器在创建 Bean 实例时,会自动调用合适的构造器,并将所需的依赖作为参数传入。
java
@Service
public class MyService {
private final Dependency dependency;
// 构造器注入
public MyService(Dependency dependency) {
this.dependency = dependency;
}
public void performAction() {
dependency.doSomething();
}
}
MyService
类需要一个 Dependency
类型的依赖,使用构造器注入后,该依赖在对象初始化时就被设置好了,但请注意:从 Spring 4.3 及以上版本 开始,如果类只有一个构造器,可以省略 @Autowired
注解,Spring 会自动识别并使用该构造器注入依赖。
为什么说构造器注入是最推荐的一种依赖注入方式🤓
保证不可变性
使用构造器注入,可以将依赖项声明为final
字段。这意味着一旦对象被创建后,它的依赖就不会再改变。这种不变性有助于确保对象在其生命周期内的状态一致性,从而减少潜在的错误。
java
public class MyService {
private final Dependency dependency;
public MyService(Dependency dependency) {
this.dependency = dependency;
}
}
在这个例子中,dependency
字段是 final
的,意味着它在对象被创建之后不能被修改。这增强了代码的安全性和可维护性。这在实战中一般通过使用 final
关键字定义依赖,防止它们在对象创建之后被修改。
保证依赖完整性
所有必须的依赖都在构造器中声明,保证了类在使用前就具备所有必需的依赖。这样错误就可以在编译阶段就可以检测出来,防止应用在运行时出现错误,也就避免了因依赖未注入而导致的 NullPointerException
等运行时错误。
易于单元测试
由于依赖是通过构造器传入的,你可以在测试中轻松地传入模拟对象(mock),而不需要依赖 Spring 容器。
java
@Test
void testMyService() {
Dependency mockDependency = Mockito.mock(Dependency.class);
MyService service = new MyService(mockDependency); // 手动注入
// ...
}
明确API设计
构造器清楚地表明了一个类所依赖的对象,提高了代码的可读性和可维护性。(这对于团队协作是非常重要的,你也不想被其他好厚米暴打吧🤣)
总结❤️
虽然说构造器注入十分推荐使用,但还是要具体情况具体实践,因为构造器注入也有其不足之处,比如要可选依赖时,这种情况更适合使用 setter 注入。如果依赖过多,构造器参数列表会变得冗长,影响可读性等问题,也可以尝试用字段注入或者Builder模式优化。总之一句话,具体情况具体实践!
如果你看了这篇文章有收获可以点赞+关注+收藏🤩,这是对笔者更新的最大鼓励!如果你有更多方案或者文章中有错漏之处,请在评论区提出帮助笔者勘误,祝你拿到更好的offer!