
在 Spring 应用中,若将 AccountValidator 等有状态对象通过 new 实例化而非依赖注入,会导致依赖不可见、难以测试和生命周期失控;正确做法是将其声明为 Spring 管理的 Bean(如 prototype 作用域),并通过 ObjectProvider 按需获取,兼顾可测性、解耦性与线程安全性。
在 Spring 应用中,若将 `AccountValidator` 等有状态对象通过 `new` 实例化而非依赖注入,会导致依赖不可见、难以测试和生命周期失控;正确做法是将其声明为 Spring 管理的 Bean(如 prototype 作用域),并通过 `ObjectProvider` 按需获取,兼顾可测性、解耦性与线程安全性。
在典型的 Spring 构造注入实践中,一个常见反模式是:服务类(如 MyService)内部直接 new AccountValidator(account) —— 这看似简洁,实则将 AccountValidator 的创建逻辑硬编码,造成依赖隐藏(hidden dependency)。该对象虽参与核心业务逻辑(如账户校验),却未出现在构造函数参数中,导致以下问题:
- 可测试性受损:单元测试 MyService 时无法轻松 Mock 或替换 AccountValidator,被迫耦合其真实行为;
- 生命周期失控:若 AccountValidator 持有非线程安全状态(如临时缓存、上下文字段),手动 new 会绕过 Spring 的作用域管理,引发并发风险;
- 职责混淆:MyService 不应承担对象创建职责,这违背单一职责原则。
✅ 推荐方案:将 AccountValidator 声明为 Spring Bean,并使用 ObjectProvider 按需获取
Spring 支持多种作用域。对于有状态、需每次调用都获得新实例的验证器,应使用 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE),并配合 ObjectProvider<T> 注入 —— 它是 Spring 5.1+ 推荐的“延迟按需获取 prototype Bean”的安全方式(相比 ApplicationContext.getBean() 更解耦、更测试友好)。
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AccountValidator {
private final Account account;
private int validationAttempts = 0; // 示例:有状态字段
public AccountValidator(Account account) {
this.account = Objects.requireNonNull(account);
}
public boolean isValid(Account input) {
validationAttempts++;
return input != null && !input.isBlocked();
}
}
@Service
public class MyService {
private final AuxiliaryService auxiliaryService;
private final ObjectProvider<AccountValidator> validatorProvider; // ✅ 按需获取,非单例持有
public MyService(AuxiliaryService auxiliaryService,
ObjectProvider<AccountValidator> validatorProvider) {
this.auxiliaryService = auxiliaryService;
this.validatorProvider = validatorProvider;
}
public boolean isAccountValid(Account account) {
// 每次调用都获得全新、独立的 AccountValidator 实例
AccountValidator validator = validatorProvider.getObject();
boolean isValid = validator.isValid(account);
if (isValid) {
auxiliaryService.openAccount(account);
}
return isValid;
}
}? 关键优势说明:
- 依赖显式化:ObjectProvider<AccountValidator> 明确出现在构造函数中,所有依赖一目了然;
- 测试友好:在测试中可轻松注入 Mock ObjectProvider,返回可控的 Mock AccountValidator;
- 线程安全:SCOPE_PROTOTYPE 保证每次 getObject() 返回新实例,天然隔离状态;
- 零工厂污染:无需为每个有状态类编写冗余的 AccountValidatorFactory,符合 DRY 原则;
- Spring 集成深度:支持 @PostConstruct、@PreDestroy、AOP 代理等完整生命周期特性。
⚠️ 注意事项:
- 避免直接注入 AccountValidator(无 ObjectProvider 包裹),否则 Spring 会尝试注入单例实例,导致状态共享;
- 若验证器完全无状态(纯函数式),可改为 @Scope(SCOPE_SINGLETON) 并直接注入,提升性能;
- ObjectProvider.getObject() 在 Bean 不存在时默认返回 null,如需强制存在,可用 getIfAvailable() 或 getIfUnique() 配合断言。
综上,将有状态验证器作为 prototype Bean + ObjectProvider 注入,是 Spring 生态下兼顾清晰性、可测性与工程健壮性的标准实践。