Java 使用GoogleGuice上下文绑定两个不同的接口实现

Java 使用GoogleGuice上下文绑定两个不同的接口实现,java,servlets,guice,Java,Servlets,Guice,让我们假设我有一个名为IValidator的接口,它看起来如下所示: public interface IValidator { /** * Returns true if the specified strings are valid. */ public boolean validate(List<String> someStrings); } @Inject public MyServlet(@Named("strict")

让我们假设我有一个名为
IValidator
的接口,它看起来如下所示:

public interface IValidator {
    /**
     * Returns true if the specified strings are valid.
     */
    public boolean validate(List<String> someStrings);
}
    @Inject
    public MyServlet(@Named("strict") final IValidator validator) {
        this.validator = validator;
    }
    @Inject
    public void setValidator(@Named("laissezfaire") final IValidator validator) {
        this.validator = validator;
    }
public class ValidatorModule implements Module {
    @Override
    protected void configure() {
        bind(IValiator.class).annotatedWith(Names.named("strict")).to(StrictValidator.class);
        bind(IValidator.class).annotatedWith(Names.named("laissezfaire")).to(LaissezFaireValidator.class);
    }
}
现在,让我们添加一个servlet,它使用
IValidator的注入实例:

public class StrictValidator implements IValidator {
    public boolean validate(List<String> someStrings) {
        //some strict validation code
        return false;
    }
}

public class LaissezFaireValidator implements IValidator {
    public boolean validate(List<String> someStrings) {
        //some easy-going validation code
        return true;
    }
}
@Service
@At("/rest")
public class MyServlet extends AbstractServlet {

    private final IValidator validator;

    @Inject
    public MyServlet(final IValidator validator) {
        this.validator = validator;
    }

    @Post
    @At("/validate")
    @LaissezFaire
    public Reply<?> validate(Request request) {
        //get the strings to validate out of the request object
        List<String> strings = (List<String>) restUtil.parseRequest(request, List.class);

        //validate the request
        if (!this.validator.validate(strings)) {
            return Reply.saying().status(409);
        } else {
            return Reply.saying().noContent();
        }
    }
}
public class MyServlet extends AbstractServlet {

    private final IValidator validator;

    @Inject
    public MyServlet(final IValidator validator) {
        this.validator = validator;
    }

    //... there's more code here (look above) ...
}
public class LaissezFaireInterceptor implements MethodInterceptor {

    private final IValidator validator;

    //... a bunch of other code goes here (see above) ...

    @Inject
    public void setValidator(final LaissezFaireValidator validator) {
        this.validator = validator;
    }

    //... and a bunch more code goes here (again, see above) ...
}
但是,如果我想在一种情况下有条件地将
IValidator
绑定到
StrictValidator
,而在另一种情况下将其绑定到
LaissezFaireValidator
,会发生什么

您是否注意到上面
MyServlet.validate
上的
@LaissezFaire
注释?这是一个拦截器,看起来像这样:

@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LaissezFaire { }

public class LaissezFaireInterceptor implements MethodInterceptor {
    private boolean debug;
    private IValidator validator;

    @Inject
    public void setDebug(@Named("debug.enabled") boolean debugEnabled) {
        this.debug = debugEnabled;
    }

    @Inject
    public void setValidator(final IValidator validator) {
        this.validator = validator;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (debug) {
            if (!this.validator.validate(strings)) {
                return Reply.saying().status(409);
            } else {
                return Reply.saying().noContent();
            }
        } else {
            return invocation.proceed();
        }
    }
}
public class ValidatorModule implements Module {
    @Override
    protected void configure() {
        //in the default case, inject an instance of StrictValidator
        bind(IValiator.class).to(StrictValidator.class);
    }
}
我们再次需要一些绑定来设置拦截器:

public class InterceptorModule implements Module {
    @Override
    protected void configure() {
        final MethodInterceptor lfInterceptor = new LaissezFaireInterceptor();
        requestInjection(lfInterceptor);
        bindInterceptor(Matchers.subclassesOf(AbstractServlet.class), Matchers.AnnotatedWith(LaissezFaire.class), lfInterceptor);
    }
}
根据
ValidatorModule
,当
interceptor
调用
requestInjection(lfInterceptor)时,
LaissezFaireInterceptor
类将获得
StrictValidator
的实例

相反,我希望
MyServlet
获取
StrictValidator
的实例,并
LaissezFaireInterceptor
获取
LaissezFaireValidator
的实例

根据,我可以在请求注入时使用命名注释。
MyServlet
的构造函数将被修改为如下所示:

public interface IValidator {
    /**
     * Returns true if the specified strings are valid.
     */
    public boolean validate(List<String> someStrings);
}
    @Inject
    public MyServlet(@Named("strict") final IValidator validator) {
        this.validator = validator;
    }
    @Inject
    public void setValidator(@Named("laissezfaire") final IValidator validator) {
        this.validator = validator;
    }
public class ValidatorModule implements Module {
    @Override
    protected void configure() {
        bind(IValiator.class).annotatedWith(Names.named("strict")).to(StrictValidator.class);
        bind(IValidator.class).annotatedWith(Names.named("laissezfaire")).to(LaissezFaireValidator.class);
    }
}
LaissezFaireInterceptor
setValidator
方法将被修改为如下所示:

public interface IValidator {
    /**
     * Returns true if the specified strings are valid.
     */
    public boolean validate(List<String> someStrings);
}
    @Inject
    public MyServlet(@Named("strict") final IValidator validator) {
        this.validator = validator;
    }
    @Inject
    public void setValidator(@Named("laissezfaire") final IValidator validator) {
        this.validator = validator;
    }
public class ValidatorModule implements Module {
    @Override
    protected void configure() {
        bind(IValiator.class).annotatedWith(Names.named("strict")).to(StrictValidator.class);
        bind(IValidator.class).annotatedWith(Names.named("laissezfaire")).to(LaissezFaireValidator.class);
    }
}
最后,
validator模块
将被修改为如下所示:

public interface IValidator {
    /**
     * Returns true if the specified strings are valid.
     */
    public boolean validate(List<String> someStrings);
}
    @Inject
    public MyServlet(@Named("strict") final IValidator validator) {
        this.validator = validator;
    }
    @Inject
    public void setValidator(@Named("laissezfaire") final IValidator validator) {
        this.validator = validator;
    }
public class ValidatorModule implements Module {
    @Override
    protected void configure() {
        bind(IValiator.class).annotatedWith(Names.named("strict")).to(StrictValidator.class);
        bind(IValidator.class).annotatedWith(Names.named("laissezfaire")).to(LaissezFaireValidator.class);
    }
}
这一切都很好,只是文档特别指出要避免这种方法,因为编译器无法检查字符串名称。此外,这意味着我必须在代码中通过注入请求
IValidator
的每个位置添加一个名为
@的
注释,否则绑定将失败

我真的希望它能为我解决这个问题,但他们似乎对绑定的上下文一无所知。因为他们不知道请求绑定的类的类型,所以我无法选择从
get()
方法返回哪种类型的
IValidator

有更好的方法解决这个问题吗?

虽然提供了一些很好的建议,但我们选择了一个更简单的解决方案来解决这个问题

如上所述,我们创建了
IValidator
接口,以及
StrictValidator
LaissezFaireValidator
类。在默认情况下,我们使用
validator模块
IValidator
绑定到
StrictValidator
。作为提醒,它看起来是这样的:

@BindingAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LaissezFaire { }

public class LaissezFaireInterceptor implements MethodInterceptor {
    private boolean debug;
    private IValidator validator;

    @Inject
    public void setDebug(@Named("debug.enabled") boolean debugEnabled) {
        this.debug = debugEnabled;
    }

    @Inject
    public void setValidator(final IValidator validator) {
        this.validator = validator;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (debug) {
            if (!this.validator.validate(strings)) {
                return Reply.saying().status(409);
            } else {
                return Reply.saying().noContent();
            }
        } else {
            return invocation.proceed();
        }
    }
}
public class ValidatorModule implements Module {
    @Override
    protected void configure() {
        //in the default case, inject an instance of StrictValidator
        bind(IValiator.class).to(StrictValidator.class);
    }
}
在绝大多数情况下,
StrictValidator
是必需的实现,因为
LaissezFaireInterceptor
是用于测试的欺骗

无论我们想要一个
StrictValidator
(就像我们在
MyServlet
中所做的那样),我们都注入了一个
ivalidater
的实例:

public class StrictValidator implements IValidator {
    public boolean validate(List<String> someStrings) {
        //some strict validation code
        return false;
    }
}

public class LaissezFaireValidator implements IValidator {
    public boolean validate(List<String> someStrings) {
        //some easy-going validation code
        return true;
    }
}
@Service
@At("/rest")
public class MyServlet extends AbstractServlet {

    private final IValidator validator;

    @Inject
    public MyServlet(final IValidator validator) {
        this.validator = validator;
    }

    @Post
    @At("/validate")
    @LaissezFaire
    public Reply<?> validate(Request request) {
        //get the strings to validate out of the request object
        List<String> strings = (List<String>) restUtil.parseRequest(request, List.class);

        //validate the request
        if (!this.validator.validate(strings)) {
            return Reply.saying().status(409);
        } else {
            return Reply.saying().noContent();
        }
    }
}
public class MyServlet extends AbstractServlet {

    private final IValidator validator;

    @Inject
    public MyServlet(final IValidator validator) {
        this.validator = validator;
    }

    //... there's more code here (look above) ...
}
public class LaissezFaireInterceptor implements MethodInterceptor {

    private final IValidator validator;

    //... a bunch of other code goes here (see above) ...

    @Inject
    public void setValidator(final LaissezFaireValidator validator) {
        this.validator = validator;
    }

    //... and a bunch more code goes here (again, see above) ...
}
无论我们想要一个
LaissezFaireValidator
的实例,我们都要求注入它的具体实现来代替
IValidator

public class StrictValidator implements IValidator {
    public boolean validate(List<String> someStrings) {
        //some strict validation code
        return false;
    }
}

public class LaissezFaireValidator implements IValidator {
    public boolean validate(List<String> someStrings) {
        //some easy-going validation code
        return true;
    }
}
@Service
@At("/rest")
public class MyServlet extends AbstractServlet {

    private final IValidator validator;

    @Inject
    public MyServlet(final IValidator validator) {
        this.validator = validator;
    }

    @Post
    @At("/validate")
    @LaissezFaire
    public Reply<?> validate(Request request) {
        //get the strings to validate out of the request object
        List<String> strings = (List<String>) restUtil.parseRequest(request, List.class);

        //validate the request
        if (!this.validator.validate(strings)) {
            return Reply.saying().status(409);
        } else {
            return Reply.saying().noContent();
        }
    }
}
public class MyServlet extends AbstractServlet {

    private final IValidator validator;

    @Inject
    public MyServlet(final IValidator validator) {
        this.validator = validator;
    }

    //... there's more code here (look above) ...
}
public class LaissezFaireInterceptor implements MethodInterceptor {

    private final IValidator validator;

    //... a bunch of other code goes here (see above) ...

    @Inject
    public void setValidator(final LaissezFaireValidator validator) {
        this.validator = validator;
    }

    //... and a bunch more code goes here (again, see above) ...
}
通过这种方式,我们能够基于注入的上下文有条件地注入所需的实现,而不引入任何额外的注释或工厂


当然,它并没有像它可能的那样简单,但它可以工作。

实际的绑定注释怎么样:
@BindingAnnotation@Target({FIELD,PARAMETER,METHOD})@Retention(RUNTIME)public@interface Strict{}
?您也可以尝试@condit绑定注释比我提到的@Named注释更干净,因为编译器可以检查它,但它仍然需要我对我请求注入
IValidator
实例的每个地方进行注释,所以这不是一个巨大的改进。那么类型侦听器呢?+1。直接请求所需的依赖项,而不是将其隐藏在方法调用或类上。