依赖注入详解
什么是依赖注入?
依赖注入(Dependency Injection, DI)是一种设计模式,用于实现控制反转(IoC)。它允许我们将对象的依赖关系从代码中移除,交给外部容器管理。
依赖注入的方式
1. 构造器注入(推荐)
构造器注入是最推荐的方式,它保证了依赖的不可变性和完整性。
java
@Service
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
// Spring 4.3+单构造器可省略@Autowired
public UserService(UserRepository userRepository,
EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
public void registerUser(User user) {
userRepository.save(user);
emailService.sendWelcomeEmail(user);
}
}优点:
- 依赖明确,必需的依赖在对象创建时就提供
- 支持final字段,保证不可变性
- 易于测试,可以直接通过构造器传入mock对象
- 防止循环依赖
2. Setter注入
通过Setter方法注入依赖,适用于可选依赖。
java
@Service
public class OrderService {
private NotificationService notificationService;
@Autowired(required = false)
public void setNotificationService(NotificationService notificationService) {
this.notificationService = notificationService;
}
public void placeOrder(Order order) {
// 处理订单
if (notificationService != null) {
notificationService.notifyUser(order);
}
}
}3. 字段注入(不推荐)
直接在字段上使用@Autowired注解。
java
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
// 不推荐:难以测试,隐藏依赖关系
}缺点:
- 无法创建不可变对象
- 违反单一职责原则(容易添加过多依赖)
- 难以进行单元测试
- 隐藏类的依赖关系
@Autowired注解
基本使用
java
@Service
public class BookService {
private final BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}required属性
java
@Service
public class ReportService {
private final DataSource dataSource;
private CacheManager cacheManager;
@Autowired
public ReportService(DataSource dataSource) {
this.dataSource = dataSource;
}
// 可选依赖
@Autowired(required = false)
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
}注入集合
java
@Component
public class PaymentProcessor {
private final List<PaymentProvider> paymentProviders;
@Autowired
public PaymentProcessor(List<PaymentProvider> paymentProviders) {
this.paymentProviders = paymentProviders;
}
public void processPayment(Payment payment) {
for (PaymentProvider provider : paymentProviders) {
if (provider.supports(payment)) {
provider.process(payment);
break;
}
}
}
}@Qualifier注解
当有多个相同类型的Bean时,使用@Qualifier指定具体的Bean。
java
@Configuration
public class DataSourceConfig {
@Bean("primaryDataSource")
public DataSource primaryDataSource() {
return new HikariDataSource();
}
@Bean("secondaryDataSource")
public DataSource secondaryDataSource() {
return new HikariDataSource();
}
}
@Service
public class UserService {
private final DataSource dataSource;
@Autowired
public UserService(@Qualifier("primaryDataSource") DataSource dataSource) {
this.dataSource = dataSource;
}
}@Primary注解
标记首选的Bean,当有多个候选Bean时优先选择。
java
@Configuration
public class CacheConfig {
@Bean
@Primary
public CacheManager redisCacheManager() {
return new RedisCacheManager();
}
@Bean
public CacheManager memoryCacheManager() {
return new MemoryCacheManager();
}
}
@Service
public class ProductService {
// 会注入redisCacheManager
@Autowired
private CacheManager cacheManager;
}@Resource和@Inject
@Resource (JSR-250)
java
@Service
public class CustomerService {
@Resource(name = "customerRepository")
private CustomerRepository repository;
}@Inject (JSR-330)
java
@Service
public class OrderService {
@Inject
private OrderRepository orderRepository;
}循环依赖
问题示例
java
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
}
@Service
public class ServiceB {
@Autowired
private ServiceA serviceA;
}解决方案
- 重新设计(最佳方案)
java
@Service
public class ServiceA {
private final CommonService commonService;
@Autowired
public ServiceA(CommonService commonService) {
this.commonService = commonService;
}
}
@Service
public class ServiceB {
private final CommonService commonService;
@Autowired
public ServiceB(CommonService commonService) {
this.commonService = commonService;
}
}- 使用@Lazy
java
@Service
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}- Setter注入
java
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}最佳实践
优先使用构造器注入
- 保证依赖不可变
- 使依赖关系明确
- 便于单元测试
避免字段注入
- 难以测试
- 隐藏依赖关系
使用final修饰依赖
javaprivate final UserRepository userRepository;合理使用@Qualifier和@Primary
- 多实现时明确指定
- 设置默认实现
避免循环依赖
- 重新设计类结构
- 考虑是否违反单一职责原则
示例项目
完整的依赖注入示例:
java
// Repository层
public interface UserRepository {
User findById(Long id);
void save(User user);
}
@Repository
public class UserRepositoryImpl implements UserRepository {
@Override
public User findById(Long id) {
// 实现查询逻辑
return null;
}
@Override
public void save(User user) {
// 实现保存逻辑
}
}
// Service层
@Service
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
private final Logger logger = LoggerFactory.getLogger(UserService.class);
public UserService(UserRepository userRepository,
EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
public User getUserById(Long id) {
logger.info("查询用户: {}", id);
return userRepository.findById(id);
}
public void registerUser(User user) {
userRepository.save(user);
emailService.sendWelcomeEmail(user.getEmail());
logger.info("用户注册成功: {}", user.getUsername());
}
}
// Controller层
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
@PostMapping
public void createUser(@RequestBody User user) {
userService.registerUser(user);
}
}总结
依赖注入是Spring框架的核心特性,正确使用DI可以:
- 降低代码耦合度
- 提高代码可测试性
- 便于维护和扩展
- 提升代码质量
推荐使用构造器注入,避免字段注入,合理处理循环依赖问题。