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

在 Spring 应用中,若将 AccountValidator 等有状态对象通过 new 实例化而非依赖注入,会导致依赖不可见、难以测试和生命周期失控;正确做法是将其声明为 Spring 管理的 Bean(如 prototype 作用域),并通过 ObjectProvider 按需获取,兼顾可测性、解耦性与线程安全性。

在 Spring 应用中,若将 `AccountValidator` 等有状态对象通过 `new` 实例化而非依赖注入,会导致依赖不可见、难以测试和生命周期失控;正确做法是将其声明为 Spring 管理的 Bean(如 prototype 作用域),并通过 `ObjectProvider` 按需获取,兼顾可测性、解耦性与线程安全性。

在典型的 Spring 构造注入实践中,一个常见反模式是:服务类(如 MyService)内部直接 new AccountValidator(account) —— 这看似简洁,实则将 AccountValidator 的创建逻辑硬编码,造成依赖隐藏(hidden dependency)。该对象虽参与核心业务逻辑(如账户校验),却未出现在构造函数参数中,导致以下问题:

推荐方案:将 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;
    }
}

? 关键优势说明

⚠️ 注意事项

综上,将有状态验证器作为 prototype Bean + ObjectProvider 注入,是 Spring 生态下兼顾清晰性、可测性与工程健壮性的标准实践。

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