使用 Google Guice 在运行时注入依赖项

Inject Dependencies at runtime using Google Guice

我正在考虑在我的应用程序中使用 Guice for DI,我应该能够在运行时交换实现。下面提供了一个示例来说明要求:

class ValidationEngine {
   public void validate(ValidationService vs) {}
}

class Client1_ValidationService implements ValidationService {}
class Client2_ValidationService implements ValidationService {}

上述实现之一应绑定为在运行时根据客户端名称验证函数,例如 Client1 或 Client2

我想像这样更改 ValidationEngine:

class ValidationEngine {   
    @Inject 
    @Named("vServicee") ValidationService vs;

    public void validate() {
         vs.validate()
    }
}

上述方法的问题在于@Named 注解的参数是静态的;事实上注释不接受运行时值。 Guice中还有其他方法可以解决这类问题吗?

您可以尝试使用 Injector.createChildInjector 为每个 ValidationService 实现创建您自己的注入器:

Injector client1Injector = injector.createChildInjector(new Module() {
    @Override
    public void configure(final Binder binder) {
        binder
           .bind(ValidationService.class)
           .to(Client1_ValidationService.class);
        }
    });

ValidationEngine client1Engine = 
    client1Injector.getInstance(ValidationEngine.class)

但这意味着您必须以某种方式管理所有子注射器。

您可以通过它的构造函数将配置信息传递给您的 Guice 模块。

伪代码:

main() { // your main method
    flags = parseFlags()
    injector = guice.createInjector(new MyModule(flags.validator))
}

MyModule { // your guice module
    constructor(validator): this.validator = validator;
    configure() {
        Class<ValidatorService> client_validator;
        if this.validator == KNOWN_CLIENT1:
            client_validator = Client1_ValidationService.class
        else:
            client_validator = Client2_ValidationService.class
        bind(ValidationService.class).to(client_validator);
    }
}

Guice 警告不要这样做,因为它会增加您测试的表面积。 https://github.com/google/guice/wiki/AvoidConditionalLogicInModules

1) 直接通过注入器注入名称,例如:

@Inject
private Injector injector;

public ValidationService getValidationServiceForClient(String clientName) {
    return injector.getInstance(Key.get(ValidationService.class, Names.named(clientName)));
}

2)另一种方式:

public class ValidationServiceProviderImpl implements ValidationServiceProvider {

    @Inject
    @Named("ClientA")
    private ValidationService clientAValidationService;

    @Inject
    @Named("ClientB")
    private ValidationService clientBValidationService;

    public ValidationService getValidationServiceForClient(String clientName) {
        switch (clientName) {
            case "ClientA": return clientAValidationService;
            case "ClientB": return clientBValidationService;
        }
        return null; // return default validationService for any other client
    }
}

public interface ValidationServiceProvider {
    ValidationService getValidationServiceForClient(String clientName);
}

配置:

Injector injector = Guice.createInjector(new AbstractModule() {
    protected void configure() {
        bind(ValidationService.class)
            .annotatedWith(Names.named("ClientA"))
            .to(ClientA_ValidationService.class);
        bind(ValidationService.class)
            .annotatedWith(Names.named("ClientB"))
            .to(ClientB_ValidationService.class);
        bind(ValidationServiceProvider.class)
            .to(ValidationServiceProviderImpl.class);
    }
});

使用提供商的示例:

ValidationServiceProvider validationServiceFactory = 
    injector.getInstance(ValidationServiceProvider.class);
ValidationService clientA = 
    validationServiceFactory.getValidationServiceForClient("ClientA");
ValidationService clientB = 
    validationServiceFactory.getValidationServiceForClient("ClientB");

直接使用注入器的例子:

ValidationService clientA = 
    injector.getInstance(Key.get(ValidationService.class, Names.named("ClientA")));
ValidationService clientB =
    injector.getInstance(Key.get(ValidationService.class, Names.named("ClientB")));