Spring核心概念解析:IoC/DI、Bean作用域与SpEL详解
Spring核心概念深度解析:IoC/DI、Bean作用域与SpEL
一、控制反转(IoC)与依赖注入(DI)
容器管理Bean的生命周期
Spring容器负责管理Bean的完整生命周期,从创建到销毁。典型生命周期如下:
实践建议:
- 对于轻量级初始化逻辑使用
@PostConstruct
注解方法 - 对于重量级资源释放使用
@PreDestroy
注解方法 - 实现
*Aware
接口时要谨慎,这会增加与Spring框架的耦合
依赖注入的三种方式
构造器注入(Spring官方推荐)
@Service public class OrderService { private final PaymentService paymentService; @Autowired // Spring 4.3+ 可省略 public OrderService(PaymentService paymentService) { this.paymentService = paymentService; } }
Setter注入
@Service public class UserService { private UserRepository userRepository; @Autowired public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } }
字段注入(不推荐)
@Service public class ProductService { @Autowired private ProductRepository productRepository; }
实践建议:
- 强制依赖使用构造器注入
- 可选依赖使用Setter注入
- 避免字段注入(不利于测试和不变性)
@Autowired与@Resource的区别
特性 | @Autowired | @Resource |
---|---|---|
来源 | Spring框架 | JSR-250标准 |
默认注入方式 | 按类型 | 按名称 |
名称限定 | @Qualifier | name属性 |
是否支持构造函数 | 支持 | 不支持 |
是否支持required | 支持 | 不支持 |
// @Autowired使用示例
@Service
public class ExampleService {
@Autowired
@Qualifier("mainDataSource")
private DataSource dataSource;
}
// @Resource使用示例
@Service
public class ExampleService {
@Resource(name = "mainDataSource")
private DataSource dataSource;
}
二、Bean的作用域
Spring支持以下Bean作用域:
作用域 | 描述 | 适用场景 |
---|---|---|
singleton | 容器中只存在一个实例(默认) | 无状态服务类 |
prototype | 每次请求都创建新实例 | 有状态对象 |
request | 每个HTTP请求一个实例 | Web请求相关对象 |
session | 每个HTTP会话一个实例 | 用户会话相关对象 |
application | ServletContext生命周期 | 全局应用对象 |
websocket | WebSocket会话生命周期 | WebSocket相关对象 |
配置方式:
// Java配置方式
@Bean
@Scope("prototype")
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
// 注解方式
@Service
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedService {
// ...
}
实践建议:
- 默认使用singleton,减少对象创建开销
- 有状态Bean使用prototype
- Web作用域Bean要配合proxyMode使用
- 注意线程安全问题,特别是prototype作用域
三、SpEL(Spring表达式语言)
基本语法
SpEL支持丰富的表达式功能:
// 字面量表达式
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'");
String message = (String) exp.getValue();
// 方法调用
parser.parseExpression("'Hello World'.concat('!')").getValue();
// 属性访问
parser.parseExpression("'Hello World'.bytes").getValue();
// 运算符
parser.parseExpression("2 * 3 + 5").getValue(Integer.class);
在Spring配置中的应用
注解中的SpEL:
@Value("#{systemProperties['db.username']}") private String username; @Value("#{ T(java.lang.Math).random() * 100.0 }") private double randomNumber;
XML配置中的SpEL:
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"> <property name="jdbcUrl" value="#{systemEnvironment['DB_URL']}"/> <property name="username" value="#{systemEnvironment['DB_USER']}"/> </bean>
条件化Bean:
@Bean @ConditionalOnExpression("#{environment.getProperty('app.feature.enabled') == 'true'") public FeatureService featureService() { return new FeatureServiceImpl(); }
安全考虑
SpEL表达式可能成为注入攻击的入口,使用时应注意:
- 避免使用用户输入直接构建表达式
- 对于复杂表达式考虑使用自定义PermissionEvaluator
- 在生产环境中限制评估上下文
实践建议:
- 使用SpEL简化配置,特别是环境相关的值
- 避免在SpEL中编写复杂业务逻辑
- 对用户提供的表达式要进行严格校验
总结
Spring的核心概念构成了整个框架的基础:
- IoC/DI实现了松耦合的组件管理
- Bean作用域提供了灵活的对象生命周期控制
- SpEL增强了配置的灵活性和表达能力
掌握这些核心概念后,开发者可以更高效地使用Spring框架构建可维护的应用程序。建议在实际项目中根据具体需求选择合适的依赖注入方式,合理规划Bean的作用域,并适度使用SpEL提升配置的灵活性。