在Java企业级开发领域,Spring Framework的依赖注入(Dependency Injection,简称DI)机制彻底改变了我们管理对象依赖关系的方式。记得2004年第一次接触Spring 1.0时,那些XML配置虽然繁琐,但已经展现出解耦的威力。如今DI已成为现代Java开发的标配,其核心思想是:对象不再自行创建或查找依赖项,而是由外部容器在运行时注入。
这种控制反转(IoC)带来的直接好处是:
重要提示:虽然现在流行注解配置,但理解XML配置方式对掌握DI原理很有帮助。建议新手从XML配置开始学习,再过渡到注解方式。
这是Spring团队最推荐的方式,通过构造方法注入依赖:
java复制public class OrderService {
private final PaymentGateway paymentGateway;
@Autowired
public OrderService(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
}
优势:
我在电商项目中实测发现:当有多个构造方法时,Spring 4.3+可以省略@Autowired注解,但建议显式声明以提高可读性。
通过setter方法注入依赖:
java复制public class ProductService {
private InventoryService inventoryService;
@Autowired
public void setInventoryService(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
}
适用场景:
踩坑记录:循环依赖问题在Setter注入时更常见,建议优先使用构造器注入避免这类问题。
直接通过字段注入:
java复制public class UserService {
@Autowired
private UserRepository userRepository;
}
虽然写法简洁,但存在明显问题:
实际项目中,我们只在测试代码或配置类中使用字段注入,生产代码坚持使用构造器注入。
Spring提供了多种条件化注入方式:
java复制@Bean
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public CacheManager cacheManager() {
return new RedisCacheManager();
}
java复制@Configuration
@Profile("production")
public class ProdConfig {
@Bean
public DataSource prodDataSource() {
// 生产环境数据源配置
}
}
java复制public class MongoDBAvailableCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 检查MongoDB是否可用
}
}
Spring支持灵活的多实现类注入:
java复制public class ReportGenerator {
private final List<DataProcessor> processors;
@Autowired
public ReportGenerator(List<DataProcessor> processors) {
this.processors = processors.stream()
.sorted(Comparator.comparingInt(DataProcessor::getOrder))
.collect(Collectors.toList());
}
}
可以通过@Order注解或实现Ordered接口控制处理顺序:
java复制@Component
@Order(2)
public class ValidationProcessor implements DataProcessor {
// 实现代码
}
对于大型应用,可以使用延迟初始化提高启动速度:
java复制@Configuration
@Lazy
public class HeavyConfig {
@Bean
@Lazy
public ResourceIntensiveBean heavyBean() {
return new ResourceIntensiveBean();
}
}
但要注意:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| NoSuchBeanDefinitionException | 1. 未添加@Component注解 2. 扫描路径未包含 3. @Conditional条件不满足 |
1. 检查组件注解 2. 确认@ComponentScan配置 3. 检查条件表达式 |
| NoUniqueBeanDefinitionException | 存在多个同类型Bean | 1. 使用@Qualifier指定名称 2. 使用@Primary标记首选Bean |
| BeanCurrentlyInCreationException | 循环依赖 | 1. 改为构造器注入 2. 使用@Lazy延迟初始化 |
java复制@Configuration
@EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
@ComponentScan(basePackages = "com.your.package")
public class AppConfig {
@Bean
@Primary
public DataSource dataSource() {
// 明确配置主数据源
}
}
java复制@TestConfiguration
public class TestConfig {
@Bean
@Primary
public UserRepository mockUserRepository() {
return Mockito.mock(UserRepository.class);
}
}
随着Spring Boot和Spring Cloud的普及,DI使用方式也在进化:
java复制@ConfigurationProperties(prefix = "app.mail")
public class MailProperties {
private String host;
private int port;
// getters/setters
}
java复制@Configuration
public class FunctionalConfig {
@Bean
public ApplicationContextInitializer<GenericApplicationContext> beanRegistrar() {
return context -> {
context.registerBean(MyService.class, () -> new MyService());
context.registerBean(OtherService.class,
() -> new OtherService(context.getBean(MyService.class)));
};
}
}
在微服务架构下,我推荐采用分层注入策略:
这种分层方式既保持了灵活性,又确保了代码的可测试性和可维护性。