Spring IOC/DI的依赖注入方式及示例
Spring IOC/DI的依赖注入方式及示例
Spring的依赖注入(DI)支持多种方式,以下是核心方式及示例:
1. 构造器注入(Constructor Injection)
通过构造函数传递依赖,确保依赖在对象初始化时存在,推荐用于必填依赖。
示例代码:
// 依赖接口
public interface UserDao {
void save();
}
// 依赖实现
@Component
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("User saved");
}
}
// 服务类(通过构造函数注入)
@Service
public class UserService {
private final UserDao userDao;
// 使用 @Autowired 的构造函数注入
@Autowired
public UserService(UserDao userDao) {
this.userDao = userDao;
}
public void createUser() {
userDao.save();
}
}
注释说明:
@Autowired
标注构造函数,Spring会自动注入UserDao
的实现。userDao
被标记为final
,确保不可变性。
2. 字段注入(Field Injection)
直接在字段上使用 @Autowired
,由Spring自动注入,代码简洁但需谨慎使用。
示例代码:
// 依赖接口和实现与上同
// 服务类(通过字段注入)
@Service
public class UserService {
@Autowired // 直接在字段上标注
private UserDao userDao;
public void createUser() {
userDao.save();
}
}
注释说明:
- 字段直接标注
@Autowired
,无需构造函数或Setter方法。 - 缺点:可能绕过对象初始化逻辑,字段可能为
null
(如延迟加载)。
3. Setter方法注入(Setter Injection)
通过Setter方法注入依赖,支持可选依赖或动态修改。
示例代码:
// 依赖接口和实现与上同
// 服务类(通过Setter注入)
@Service
public class UserService {
private UserDao userDao;
// 使用 @Autowired 的Setter方法
@Autowired
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void createUser() {
userDao.save();
}
}
注释说明:
- 依赖通过Setter方法注入,支持可选依赖(如
@Autowired
可标记为required=false
)。
4. @Value 注入基本类型或SpEL表达式
用于注入配置属性(如字符串、数字)或表达式。
示例代码:
@ConfigurationProperties(prefix = "app.config")
@Component
public class AppConfig {
private String databaseUrl;
private int timeout;
// getters/setters
}
// 服务类(通过 @Value 注入)
@Service
public class DatabaseService {
@Value("${app.config.databaseUrl}") // 从配置文件读取
private String databaseUrl;
@Value("#{ T(java.lang.Math).random() * 100 }") // SpEL表达式
private double randomValue;
public void printConfig() {
System.out.println("Database URL: " + databaseUrl);
System.out.println("Random Value: " + randomValue);
}
}
注释说明:
@Value
支持直接注入配置文件属性(如application.properties
)或表达式。
5. @Qualifier 解决依赖歧义
当存在多个相同类型的Bean时,通过 @Qualifier
指定具体Bean。
示例代码:
// 两个相同接口的Bean
@Component
@Qualifier("primary")
public class PrimaryUserDao implements UserDao {
public void save() { System.out.println("Primary DAO"); }
}
@Component
@Qualifier("secondary")
public class SecondaryUserDao implements UserDao {
public void save() { System.out.println("Secondary DAO"); }
}
// 服务类(通过 @Qualifier 指定Bean)
@Service
public class UserService {
@Autowired
@Qualifier("primary") // 指定使用PrimaryUserDao
private UserDao userDao;
public void createUser() {
userDao.save();
}
}
对比表格
方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
构造器注入 | - 依赖必填,确保初始化完成 - 支持 final 字段- 测试友好 | - 代码冗余(需写构造函数) | 推荐:必填依赖、复杂对象初始化、测试驱动开发(TDD) |
字段注入 | - 简单快捷 | - 可能绕过构造函数逻辑 - 字段可能未初始化( null ) | 快速开发、简单场景(非核心依赖) |
Setter注入 | - 灵活支持可选依赖 | - 依赖可能延迟注入 - 难以保证线程安全 | 可选依赖、动态修改依赖场景(如配置类) |
@Value 注入 | - 直接注入配置属性或表达式 | - 仅限简单类型(需配合 @ConfigurationProperties 处理复杂配置) | 配置参数注入(如数据库URL、常量) |
@Qualifier 解歧义 | - 明确指定Bean,解决类型冲突 | - 需额外标注,增加代码复杂度 | 多个同类型Bean共存时选择具体实现 |
选择建议
- 优先使用构造器注入:确保依赖存在且易于测试。
- 字段注入:仅在简单场景或快速开发时使用。
- Setter注入:用于可选依赖或需要后期绑定的场景。
- @Value + @ConfigurationProperties:集中管理配置属性。
- @Qualifier:解决依赖冲突时明确指定Bean。