Spring IoC DI 之 属性注入
一、DI (依赖注入)
依赖注入(DI)是一种实现控制反转(IoC)的技术,可以在运行时动态地向应用程序提供其所需的依赖。在 Spring 框架中,DI 是一种常用的实践,有助于提高代码的模块化和灵活性。下面是依赖注入的一些关键点和优势:
依赖注入的工作原理
-
容器管理:
- 在 Spring 中,容器负责创建和管理对象(即 Beans)。开发者只需要通过配置(可以是 XML 文件、注解或 Java 配置类)指定依赖关系。
-
注入方式:
- 构造器注入:通过构造函数传递依赖。
- 设置器注入:通过 JavaBean 属性的 setter 方法传递依赖。
- 字段注入:直接在字段上注入依赖,通常使用
@Autowired
注解。
-
运行时绑定:
- 依赖注入是在运行时处理的,这意味着应用程序的组件可以在不修改代码的情况下,通过更改配置来使用不同的依赖实现。
二、使用 字段注入自动注入 Bean
在 Spring 的组件(如由 @Component
, @Service
, @Controller
等注解标记的类)中,你可以使用 @Autowired
注解自动注入 Bean。这是推荐的方法,因为它减少了对 Spring API 的直接依赖,并且使得代码更加简洁。
@Component
public class UserService {
@Autowired
private User user;
public void displayUser() {
System.out.println("User Name: " + user.getName());
}
}
Spring 应用中使用 @Autowired
注解自动注入 Bean 且存在多个相同类型的 Bean 时,会出现冲突,因为 Spring 不知道应该注入哪一个。为了解决这个问题,Spring 提供了几种方法来指定要注入的具体 Bean,确保准确性和灵活性。
1)指定Bean 注入
a) 使用 @Qualifier
注解
@Qualifier
注解用来进一步细化 @Autowired
的注入选择。通过为每个 Bean 定义一个名称,然后在注入时指定这个名称,可以控制哪一个 Bean 被注入。
@Component
public class VehicleService {
@Autowired
@Qualifier("car")
private Vehicle vehicle;
public void service() {
vehicle.performService();
}
}
@Component("car")
public class Car implements Vehicle {
@Override
public void performService() {
System.out.println("Servicing a car");
}
}
@Component("bike")
public class Bike implements Vehicle {
@Override
public void performService() {
System.out.println("Servicing a bike");
}
}
在这个例子中,我们有两个 Vehicle
类型的 Bean,分别是 Car
和 Bike
。使用 @Qualifier("car")
告诉 Spring 在注入 Vehicle
类型的依赖时使用名为 "car" 的 Bean
b) 使用 @Primary
注解
当你想要在多个相同类型的 Bean 中指定一个“默认”Bean 时,可以使用 @Primary
注解。当存在多个 Bean 且其中一个被标记为 @Primary
,Spring 会优先注入这个 Bean。
@Component
@Primary
public class Car implements Vehicle {
@Override
public void performService() {
System.out.println("Servicing a car");
}
}
@Component
public class Bike implements Vehicle {
@Override
public void performService() {
System.out.println("Servicing a bike");
}
}
@Component
public class VehicleService {
@Autowired
private Vehicle vehicle; // 默认注入 @Primary 标记的 Bean
public void service() {
vehicle.performService();
}
}
在这个例子中,Car
类被标记为 @Primary
,因此它会被默认注入到 VehicleService
中,除非使用 @Qualifier
明确指定其他 Bean。
c) 使用特定方法命名或 @Bean
注解中的名称
在 Java 配置类中,你可以通过方法名称或在 @Bean
注解中指定名称来控制 Bean 的标识符。然后,可以结合使用 @Autowired
和 @Qualifier
。
@Configuration
public class AppConfig {
@Bean(name = "car")
public Vehicle car() {
return new Car();
}
@Bean(name = "bike")
public Vehicle bike() {
return new Bike();
}
}
然后,使用 @Qualifier
来指定注入哪一个。
通过这些方法,你可以在 Spring 中精确控制哪个 Bean 被注入到有多个相同类型的 Bean 的情况下,从而保证应用的正确性和灵活性。
2)使用 @Resource 或 @Inject 注解
@Resource
和 @Inject
是 JSR-250 和 JSR-330 的标准 Java 注解,分别来自 Java EE 和依赖注入规范。它们与 @Autowired
类似,但略有不同的注入语义和配置方式。
示例代码(使用 @Resource
):
@Component
public class UserService {
@Resource(name="user")
private User user;
public void displayUser() {
System.out.println("User Name: " + user.getName());
}
}
三、 构造方法注入
这种方式是最推荐的,因为它确保了所有的依赖在对象被创建之前就已经准备好了。
@Component
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void performAction() {
System.out.println("Action performed with user repository.");
}
}
四、Setter 方法注入
这种方法适用于可选依赖,或者在对象创建之后还可以更改依赖的情况
@Component
public class NotificationService {
private EmailService emailService;
@Autowired
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
}
public void sendNotification(String message, String recipient) {
emailService.sendEmail(message, recipient);
}
}
@Component
public class EmailService {
public void sendEmail(String message, String recipient) {
System.out.println("Email sent to " + recipient + " with message: " + message);
}
}