Spring Bean配置与依赖注入详解
Spring容器核心:Bean配置与依赖注入详解
Spring框架的核心是它的容器,负责管理应用中各个组件(称为Bean)的创建、配置和组装。本文将深入讲解Spring容器中Bean的配置方式和依赖注入机制,这是掌握Spring框架的基础。
一、Bean配置方式
Spring提供了多种配置Bean的方式,适应不同场景和开发风格。
1. XML配置方式(传统方式)
XML是最早的Spring配置方式,虽然现在使用减少,但在一些遗留系统中仍可见。
<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.example.service.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="com.example.dao.UserDaoImpl"/>
</beans>
特点:
- 集中式配置,与Java代码分离
- 配置冗长,类型不安全
- 适合简单应用或需要频繁修改配置的场景
2. 注解配置方式(现代主流)
使用注解可以简化配置,让代码更加简洁。
核心注解:
@Component
:通用组件注解@Service
:标识服务层组件@Repository
:标识数据访问组件@Controller
:标识控制器组件
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
// ...
}
@Repository
public class UserDaoImpl implements UserDao {
// ...
}
启用注解扫描:
<context:component-scan base-package="com.example"/>
或使用Java配置:
@Configuration
@ComponentScan("com.example")
public class AppConfig {}
实践建议:
- 优先使用注解方式,代码更简洁
- 合理使用各层注解(@Service/@Repository等)提高代码可读性
- 基础包扫描路径要设置准确,避免扫描不必要的包
3. Java配置类(@Configuration)
Java配置类结合了XML的集中配置和注解的简洁性,是Spring Boot的默认方式。
@Configuration
public class AppConfig {
@Bean
public UserService userService(UserDao userDao) {
return new UserServiceImpl(userDao);
}
@Bean
public UserDao userDao() {
return new UserDaoImpl();
}
}
特点:
- 类型安全,IDE支持好
- 可以结合编程式逻辑
- 适合需要复杂初始化逻辑的Bean
4. 条件化Bean(@Conditional)
Spring 4.0引入的条件化Bean机制,可以根据特定条件决定是否创建Bean。
@Configuration
public class DataSourceConfig {
@Bean
@Conditional(ProdEnvironmentCondition.class)
public DataSource prodDataSource() {
// 生产环境数据源
}
@Bean
@Conditional(DevEnvironmentCondition.class)
public DataSource devDataSource() {
// 开发环境数据源
}
}
public class ProdEnvironmentCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return "prod".equals(context.getEnvironment().getProperty("env"));
}
}
常用条件注解:
@Profile
:基于环境profile的条件@ConditionalOnClass
:类路径存在指定类时生效@ConditionalOnProperty
:配置属性存在且有特定值时生效
实践建议:
- 使用条件化Bean实现环境相关的配置
- Spring Boot提供了丰富的
@Conditional
派生注解,优先使用 - 复杂条件判断可以实现
Condition
接口
二、依赖注入方式
依赖注入(DI)是Spring的核心特性,实现了控制反转(IoC)。
1. @Autowired自动装配
@Autowired
是Spring提供的自动装配注解。
@Service
public class OrderService {
@Autowired
private UserService userService;
// 构造器注入
@Autowired
public OrderService(InventoryService inventoryService) {
this.inventoryService = inventoryService;
}
// Setter注入
@Autowired
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
注入方式:
- 字段注入(不推荐,不利于测试)
- 构造器注入(推荐,不可变依赖)
- Setter注入(可选依赖)
实践建议:
- 优先使用构造器注入,保证依赖不可变
- 避免字段注入,不利于单元测试
- 对于可选依赖使用Setter注入
2. @Qualifier限定符
当同一类型有多个Bean时,使用@Qualifier
指定具体Bean。
public interface MessageService {
void send(String message);
}
@Service("smsService")
public class SmsService implements MessageService {
// ...
}
@Service("emailService")
public class EmailService implements MessageService {
// ...
}
@Service
public class NotificationService {
@Autowired
@Qualifier("smsService")
private MessageService messageService;
}
3. @Resource与@Inject
除了@Autowired
,Spring还支持JSR-250的@Resource
和JSR-330的@Inject
。
@Service
public class BillingService {
@Resource(name = "creditCardProcessor")
private PaymentProcessor paymentProcessor;
@Inject
private InvoiceService invoiceService;
}
对比:
特性 | @Autowired | @Resource | @Inject |
---|---|---|---|
标准 | Spring | JSR-250 | JSR-330 |
按名称装配 | 需要@Qualifier | name属性 | 需要@Named |
必需性 | required属性 | 默认必需 | 默认必需 |
构造器注入 | 支持 | 不支持 | 支持 |
实践建议:
- 新项目统一使用
@Autowired
- 需要与其他DI框架兼容时考虑
@Inject
- 遗留系统可能使用
@Resource
三、Bean作用域
Spring Bean默认是单例(Singleton)的,但也支持其他作用域。
@Service
@Scope("prototype") // 每次获取新实例
public class PrototypeService {
// ...
}
@Controller
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserPreferences {
// 每个会话一个实例
}
常用作用域:
- singleton:默认,每个容器一个实例
- prototype:每次请求新实例
- request:每个HTTP请求一个实例
- session:每个HTTP会话一个实例
- application:每个ServletContext一个实例
- websocket:每个WebSocket会话一个实例
实践建议:
- 无状态服务使用singleton(默认)
- 有状态组件根据需求选择合适作用域
- 注意prototype作用域的性能影响
- 对于request/session作用域,在非Web环境注入时需要代理
四、最佳实践总结
配置选择:
- 新项目优先使用Java配置类+注解方式
- 简单项目可以使用纯注解方式
- 复杂配置或需要动态逻辑时使用Java配置类
依赖注入:
- 强制依赖使用构造器注入
- 可选依赖使用Setter注入
- 避免字段注入
- 同一类型多个Bean时使用@Qualifier
作用域:
- 默认使用singleton
- 有状态组件选择合适作用域
- 注意作用域代理的使用
条件化Bean:
- 使用@Profile管理环境特定配置
- 复杂条件实现Condition接口
- Spring Boot的条件注解更简洁
通过合理运用Spring容器的各种特性,可以构建出灵活、可维护的应用程序架构。理解这些核心概念是掌握Spring框架的基础。