Spring注解之依赖注入@Autowired和@Resource
Spring常见的DI方式
字段注入(Field Injection)
-
在字段上使用@Autowired/Resource注解
-
字段注入是日常开发中使用最多的一种注入方式,它的实现代码如下:
@Autowired private UserService userService;
优点
属性注入最大的优点就是实现简单、使用简单,只需要给变量上添加一个注解 @Autowired,就可以在不 new 对象的情况下,直接获得注入的对象了(这就是 DI 的功能和魅力所在),所以它的优点就是使用简单。
缺点
属性注入虽然使用简单,但也存在着很多问题,甚至编译器 Idea 都会提醒 “不建议使用此注入方式”
1、功能性问题:无法注入一个不可变的对象(final 修饰的对象)
2、通用性问题:只能适应于 IoC 容器
3、设计原则问题:更容易违背单一设计原则
Setter注入(Setter Injection)
-
调用Setter的方法注入依赖
-
实现代码如下:
// Setter 注入 private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; }
从上面代码可以看出,Setter 注入比属性注入要麻烦很多。
要说 Setter 注入有什么优点的话,那么首当其冲的就是它完全符合单一职责的设计原则,因为每一个 Setter 只针对一个对象。
但它的缺点也很明显,它的缺点主要体现在以下 2 点:1、不能注入不可变对象(final 修饰的对象);
2、注入的对象可被修改。- Setter 注入提供了 setXXX 的方法,意味着你可以在任何时候、在任何地方,通过调用 setXXX 的方法来改变注入对象,所以 Setter 注入的问题是,被注入的对象可能随时被修改。
构造器注入(Constructor Injection)
-
利用构造方法的参数注入依赖
-
构造方法注入是 Spring 官方从 4.x 之后推荐的注入方式,它的实现代码如下:
// 构造方法注入 private UserService userService; @Autowired public UserController(UserService userService) { this.userService = userService; }
如果当前的类中只有一个构造方法,那么 @Autowired 也可以省略
优点
构造方法注入相比于前两种注入方法,它可以注入不可变对象,并且它只会执行一次,也不存在像 Setter 注入那样,被注入的对象随时被修改的情况,它的优点有以下 4 个:
1、可注入不可变对象(final 修饰的对象)
2、注入对象不会被修改- 构造方法注入不会像 Setter 注入那样,构造方法在对象创建时只会执行一次,因此它不存在注入对象被随时(调用)修改的情况。
3、注入对象会被完全初始化
- 因为依赖对象是在构造方法中执行的,而构造方法是在对象创建之初执行的,因此被注入的对象在使用之前,会被完全初始化,这也是构造方法注入的优点之一。
4、通用性更好
- 构造方法和属性注入不同,构造方法注入可适用于任何环境,无论是 IoC 框架还是非 IoC 框架,构造方法注入的代码都是通用的,所以它的通用性更好。
常用注入方式总结
依赖注入的常见实现方式有 3 种:属性注入、Setter 注入和构造方法注入。
- 其中属性注入的写法最简单,所以日常项目中使用的频率最高,但它的通用性不好;
- 而 Spring 官方推荐的是构造方法注入,它可以注入不可变对象,其通用性也更好;
- 如果是注入可变对象,那么可以考虑使用 Setter 注入。
三种注入方式的优缺点
- 构造器注入:强依赖性(即必须使用此依赖),不变性(各依赖不会经常变动)
- Setter注入:可选(没有此依赖也可以工作),可变(依赖会经常变动)
- Field注入:大多数情况下尽量少使用字段注入,一定要使用的话, @Resource相对@Autowired对IoC容器的耦合更低
@Autowired 和 @Resource 有什么区别
@Autowired 和 @Resource 都是 Spring/Spring Boot 项目中,用来进行依赖注入的注解。它们都提供了将依赖对象注入到当前对象的功能,但二者却有众多不同。
-
来源不同:来自不同的“父类”,@Autowired是Spring提供的,@Resource是JSR-250提供的
-
依赖查找顺序不同
依赖注入的功能,是通过先在 Spring IoC 容器中查找对象,再将对象注入引入到当前类中。
@Autowired 查找顺序: 是先根据类型(byType)查找,如果存在多个(Bean)再根据名称(byName)进行查找
@Resource 查找顺序: 是先根据名称(byName)查找,如果(根据名称)查找不到,再根据类型(byType)进行查找 -
支持的参数不同:@Autowired 只支持设置一个 required 的参数,而 @Resource 支持 7 个参数
-
适用对象不同:@Autowired可以对构造器、方法、参数、字段使用,@Resource只能对方法、字段使用
-
依赖注入的支持不同:@Autowired 支持属性注入、构造方法注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入
为什么IDEA只对@Autowired警告
在使用IDEA开发的时候有没有注意到过一个提示,在字段上使用Spring的依赖注入注解@Autowired后会出现如下警告:
Field injection is not recommended (字段注入是不被推荐的)
但是使用@Resource却不会出现此提示
-
@Autowired是Spring提供的,它是特定IoC提供的特定注解,这就导致了应用与框架的强绑定,一旦换用了其他的IoC框架,是不能够支持注入的。
-
而 @Resource是JSR-250提供的,它是Java标准,我们使用的IoC容器应当去兼容它,这样即使更换容器,也可以正常工作。
用@RequiredArgsConstructor代替@Autowired
我们都知道注入一个bean有三种方式(set注入, 构造器注入, 注解注入),spring推荐我们使用构造器的方式注入Bean。
@RequiredArgsConstructor注解,生成带有必需参数的构造函数。必需的参数是最终字段和具有约束的字段,例如@NonNull。
这个注解是基于lombok的使用时必须导入lombok包。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
同时注意几点:
声明的变量必须为final
根据构造器注入的,相当于当容器调用带有一组参数的类构造函数时,基于构造函数的 DI 就完成了,其中每个参数代表一个对其他类的依赖。基于构造方法为属性赋值,容器通过调用类的构造方法将其进行依赖注入。
当我们需要注入Bean的时候可以直接在类上添加@RequiredArgsConstructor注解使用,代替了Autowrited注解。
@RequiredArgsConstructor
@RestController
@RequestMapping("/test")
public class TestController {
private final TestService testService;
private final UserService userService;
}