Spring属性注入
外观
概述[编辑 | 编辑源代码]
Spring属性注入(Property Injection)是Spring IoC容器实现依赖注入(DI)的核心机制之一,指通过外部配置(XML/注解/JavaConfig)为Bean对象的成员变量动态赋值的过程。与构造器注入并列,是Spring解耦组件关系的两大主要方式。
属性注入的核心特点:
- 通过setter方法或字段直接赋值
- 支持简单类型(String/int等)和复杂对象(其他Bean)
- 运行时由容器自动完成依赖装配
- 符合"好莱坞原则"(不要调用我们,我们会调用你)
注入方式[编辑 | 编辑源代码]
1. XML配置注入[编辑 | 编辑源代码]
最传统的注入方式,通过<property>
标签指定:
<!-- applicationContext.xml -->
<bean id="dataSource" class="com.example.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="secret"/>
</bean>
等效的Java类定义:
public class BasicDataSource {
private String driverClassName;
private String url;
private String username;
private String password;
// Setter方法必须存在
public void setDriverClassName(String driver) {
this.driverClassName = driver;
}
// 其他setter...
}
2. 注解注入[编辑 | 编辑源代码]
Spring 2.5+支持的现代方式:
@Component
public class OrderService {
@Value("${order.max.retry}") // 注入配置值
private int maxRetry;
@Autowired // 按类型自动装配
private PaymentGateway gateway;
@Resource(name="emailService") // 按名称装配
private NotificationService notifier;
}
需配合注解扫描:
<context:component-scan base-package="com.example"/>
<context:property-placeholder location="classpath:app.properties"/>
3. JavaConfig注入[编辑 | 编辑源代码]
Spring 3.0+的纯Java配置方式:
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/mydb");
return ds;
}
}
注入类型[编辑 | 编辑源代码]
1. 简单值注入[编辑 | 编辑源代码]
支持所有基本类型及其包装类、String、Class等:
<property name="timeout" value="5000"/> <!-- 自动转换为int -->
<property name="active" value="true"/> <!-- boolean -->
2. Bean引用注入[编辑 | 编辑源代码]
注入容器中的其他Bean:
<bean id="userService" class="com.example.UserService">
<property name="userDao" ref="jdbcUserDao"/> <!-- ref指向其他bean -->
</bean>
3. 集合类型注入[编辑 | 编辑源代码]
复杂集合的配置示例:
<bean id="complexBean" class="com.example.CollectionBean">
<property name="list">
<list>
<value>item1</value>
<ref bean="specialService"/>
</list>
</property>
<property name="map">
<map>
<entry key="config1" value="100"/>
<entry key="service" value-ref="logService"/>
</map>
</property>
</bean>
高级特性[编辑 | 编辑源代码]
延迟注入[编辑 | 编辑源代码]
使用@Lazy
注解延迟初始化:
@Component
public class HeavyResource {
@Lazy
@Autowired
private ExpensiveToLoad externalService;
}
条件注入[编辑 | 编辑源代码]
Spring 4.0+的条件化装配:
@Bean
@Conditional(ProdEnvCondition.class)
public DataSource productionDataSource() {
// 仅当条件满足时创建Bean
}
SpEL表达式[编辑 | 编辑源代码]
使用Spring表达式语言动态计算值:
<property name="defaultLocale" value="#{systemProperties['user.language']}"/>
<property name="randomNumber" value="#{T(java.lang.Math).random() * 100}"/>
最佳实践[编辑 | 编辑源代码]
1. 强制依赖推荐使用构造器注入,可选依赖使用属性注入
2. 避免混合使用多种注入方式(如同时使用XML和注解)
3. 对@Autowired
添加@Qualifier
消除歧义
4. 优先使用类型安全(type-safe)的JavaConfig方式
典型应用场景[编辑 | 编辑源代码]
电商系统库存服务示例:
@Service
public class InventoryService {
@Value("${inventory.threshold}")
private int stockThreshold;
@Autowired
private WarehouseLocator locator;
@Resource
private List<InventoryValidator> validators;
// 业务方法...
}
stockThreshold
:从配置文件中注入locator
:自动装配主要仓库定位器validators
:收集所有实现InventoryValidator
接口的Bean
常见问题[编辑 | 编辑源代码]
页面模块:Message box/ambox.css没有内容。
循环依赖问题: |
当BeanA依赖BeanB,同时BeanB又依赖BeanA时,解决方案:
- 重构代码消除循环依赖
- 使用
@Lazy
延迟初始化 - 改用setter注入(而非构造器注入)
总结[编辑 | 编辑源代码]
Spring属性注入机制提供了灵活的依赖配置方式,通过本文介绍的多种实现形式和最佳实践,开发者可以构建松耦合、易测试的应用程序。随着Spring版本的演进,推荐逐步采用类型安全的JavaConfig结合注解的方式,以获得更好的开发体验和代码可维护性。