商城首页欢迎来到中国正版软件门户

您的位置:首页 >依赖注入中如何避免依赖隐藏在有状态对象中

依赖注入中如何避免依赖隐藏在有状态对象中

  发布于2026-04-13 阅读(0)

扫一扫,手机访问

依赖注入中如何避免将依赖隐藏于有状态对象(如验证器)中

在 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 生态下兼顾清晰性、可测性与工程健壮性的标准实践。

本文转载于:互联网 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注