Skip to content

依赖注入详解

什么是依赖注入?

依赖注入(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;
}

解决方案

  1. 重新设计(最佳方案)
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;
    }
}
  1. 使用@Lazy
java
@Service
public class ServiceA {
    private final ServiceB serviceB;
    
    @Autowired
    public ServiceA(@Lazy ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}
  1. Setter注入
java
@Service
public class ServiceA {
    private ServiceB serviceB;
    
    @Autowired
    public void setServiceB(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

最佳实践

  1. 优先使用构造器注入

    • 保证依赖不可变
    • 使依赖关系明确
    • 便于单元测试
  2. 避免字段注入

    • 难以测试
    • 隐藏依赖关系
  3. 使用final修饰依赖

    java
    private final UserRepository userRepository;
  4. 合理使用@Qualifier和@Primary

    • 多实现时明确指定
    • 设置默认实现
  5. 避免循环依赖

    • 重新设计类结构
    • 考虑是否违反单一职责原则

示例项目

完整的依赖注入示例:

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可以:

  • 降低代码耦合度
  • 提高代码可测试性
  • 便于维护和扩展
  • 提升代码质量

推荐使用构造器注入,避免字段注入,合理处理循环依赖问题。