Spring注解式依赖注入
Spring注解式依赖注入
在Spring框架中,依赖注入(Dependency Injection, DI)是核心功能之一,通过DI可以使得组件之间的依赖关系解耦,方便管理和维护。
依赖注入(DI)是一种软件设计模式,用于实现对象之间的松耦合。这种模式通过将对象的依赖(即被依赖的对象)注入到对象中,而不是在对象内部直接创建依赖,从而实现对象之间的解耦。DI是控制反转(Inversion of Control, IoC)的一种实现方式。
本文将详细介绍常用的注解:@Autowired、@Resource、@Qualifier、@Inject、@Lookup,并说明它们的使用场景和区别。
@Autowired
概述
@Autowired是Spring框架中用于自动注入依赖的注解。它可以标注在字段、构造方法、Setter方法上。
使用方式
字段注入:
字段式注入是项目中最常见的DI方式
1
2
3
4
5
6
7
public class MyService {
private MyRepository myRepository;
// other methods
}构造方法注入:
1
2
3
4
5
6
7
8
9
10
11
public class MyService {
private final MyRepository myRepository;
public MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
// other methods
}Setter方法注入:
1
2
3
4
5
6
7
8
9
10
11
public class MyService {
private MyRepository myRepository;
public void setMyRepository(MyRepository myRepository) {
this.myRepository = myRepository;
}
// other methods
}
工作原理
@Autowired默认按类型(by type)注入,即Spring会在容器中查找匹配类型的Bean,并注入到目标属性中。如果有多个相同类型的Bean,可以配合@Qualifier来指定具体的Bean。
常见问题
- NoUniqueBeanDefinitionException:当有多个同类型的Bean时,Spring不知道注入哪一个Bean,会抛出此异常。此时需要使用
@Qualifier来指定具体的Bean。 - NoSuchBeanDefinitionException:当找不到匹配的Bean时,会抛出此异常。可以通过
@Autowired(required = false)来避免异常,但注入的字段可能为null。
@Resource
概述
@Resource是JSR-250规范(Java Specification Request,Java 规范提案)中的注解,也可以用于注入依赖。它由Java EE提供,在Spring中也可以使用。
使用方式
@Resource注解默认按名称(by name)注入,也可以通过name属性指定具体的Bean名称。
字段注入:
1
2
3
4
5
6
7
public class MyService {
private MyRepository myRepository;
// other methods
}指定名称:
1
2
3
4
5
6
7
public class MyService {
private MyRepository myRepository;
// other methods
}
工作原理
@Resource首先按名称查找,如果没有找到匹配的Bean,再按类型查找。如果名称和类型都没有找到匹配的Bean,会抛出异常。
常见问题
- NoSuchBeanDefinitionException:当指定的名称或类型找不到匹配的Bean时,会抛出此异常。
- Overlapping:在使用
@Resource和@Autowired时,如果两个注解作用于同一个字段,可能会产生冲突,需要小心使用。
@Qualifier
概述
@Qualifier用于在自动注入时,指定具体的Bean。当有多个相同类型的Bean时,可以用@Qualifier来消除歧义。
使用方式
@Qualifier可以和@Autowired、@Inject配合使用。
配合@Autowire使用:
1
2
3
4
5
6
7
8
public class MyService {
private MyRepository myRepository;
// other methods
}配合构造方法使用:
1
2
3
4
5
6
7
8
9
10
11
public class MyService {
private final MyRepository myRepository;
public MyService( MyRepository myRepository) {
this.myRepository = myRepository;
}
// other methods
}
工作原理
@Qualifier通过指定Bean的名称来消除自动注入时的歧义。它在注入时会查找与名称匹配的Bean并注入。
常见问题
- NoSuchBeanDefinitionException:当指定的Bean名称找不到匹配的Bean时,会抛出此异常。
@Inject
概述
@Inject 是由 Java 标准规范 JSR-330 提供的注解,用于依赖注入。它和 Spring 特有的 @Autowired 非常相似,主要区别在于其来源。
使用方式
@Inject 可以标注在字段、构造函数和 Setter 方法上。
和@Autowired区别
来源:
@Inject是 JSR-330(Java Dependency Injection 标准)的一部分,而@Autowired是 Spring 特有的注解。功能:在大多数情况下,
@Inject和@Autowired的功能相同,都用于自动注入依赖。扩展性:
@Autowired有更多的功能,例如required属性,可以指定注入是否为必需的。@Inject没有这个属性。环境:
@Inject更加通用,可以在任何依赖注入框架中使用,而@Autowired主要在 Spring 框架中使用。
@Lookup
概述
@Lookup 是 Spring 特有的注解,用于解决特殊的依赖注入场景。特别是当你需要在一个单例 Bean 中注入一个原型(prototype)作用域的 Bean 时,可以使用 @Lookup 来确保每次调用都返回不同的实例。
使用方式
@Lookup 注解应用于抽象方法上,Spring 会在运行时覆盖该方法,以返回指定的 Bean 实例。
1 |
|
和其他DI注解的区别
功能:
@Lookup主要用于动态地获取 Bean 实例,特别适合于在单例 Bean 中注入原型 Bean。注入时机:
@Lookup在每次调用时都返回新的 Bean 实例,而@Autowired和@Inject在 Spring 容器启动时进行依赖注入。使用场景:
@Lookup适用于需要在运行时获取新的 Bean 实例的场景,例如需要每次调用都返回不同的原型作用域 Bean。
总结:
- @Autowired:Spring特有的注解,默认按类型注入,可以标注在字段、构造方法、Setter方法上。可以与
@Qualifier配合使用来消除歧义。 - @Resource:JSR-250规范中的注解,默认按名称注入,也可以按类型注入。主要用于字段注入。
- @Qualifier:用于在多Bean情况下指定具体的Bean,与
@Autowired、@Inject配合使用。 - @Inject:JSR-330规范中的注解,功能同(略少)@Autowired,但不局限于Spring框架
- @Lookup:Spring特有的注解,用于运行时动态获取新的 Bean 实例的场景
注解式依赖注入主要在Spring容器启动时进行。Spring容器会在启动时扫描、实例化和注入所有需要管理的Bean。
运行时注入是特殊情况,通常用于需要动态获取Bean实例的场景,如使用
@Lookup注解。
扩展:
构造函数注入
适用场景
构造函数注入是指通过类的构造函数将依赖项注入到类中。这种方式在创建对象时就完成了所有依赖项的注入。
- 不可变对象:如果对象的依赖项在创建后不会再改变,构造函数注入是一种很好的选择。它确保所有的依赖在对象创建时就完全提供。
- 强制依赖:通过构造函数注入,可以强制依赖项的存在,避免在运行时出现空指针异常。
- 单一责任原则:构造函数注入有助于遵循单一责任原则(SRP),使类的设计更清晰。类的依赖项明确地通过构造函数传递,使得类的职责更加明确。
优缺点
优点:
- 依赖项不可变,确保依赖项在对象创建时提供。
- 代码更清晰,依赖关系更显式。
- 更容易进行单元测试,可以使用Mockito等框架轻松进行依赖注入。
缺点:
- 如果依赖项较多,构造函数参数列表可能会变得很长,影响代码的可读性。
Setter方法注入
适用场景
Setter方法注入是指通过类的Setter方法将依赖项注入到类中。这种方式允许在对象创建后再进行依赖项的设置。
- 可选依赖项:如果某些依赖项是可选的,使用Setter方法注入可以在需要时提供这些依赖项。
- 配置或初始化后设置依赖:在某些情况下,依赖项可能需要在对象创建后进行配置或初始化,然后再进行注入。
优缺点
优点:
- 可以设置可选的依赖项,灵活性更高。
- 适用于需要在对象创建后进行某些初始化操作的场景。
缺点:
- 依赖项可以在对象创建后改变,可能会导致不一致的状态。
- 如果依赖项没有被设置,可能会在运行时出现空指针异常。
比较与总结
构造函数注入:
- 强制性:确保所有依赖项在对象创建时提供,适用于强制依赖。
- 不变性:一旦对象创建,依赖项不可改变,确保对象状态的一致性。
- 代码清晰:依赖关系显式在构造函数中体现。
Setter方法注入:
- 灵活性:允许在对象创建后设置或更改依赖项,适用于可选依赖。
- 初始化后设置:适用于需要在对象创建后进行某些初始化操作的场景。
- 潜在风险:可能导致对象状态不一致,需确保所有依赖项在使用前正确设置。
