假设我有一个名为IValidator
的接口,该接口如下所示:
public interface IValidator {
/**
* Returns true if the specified strings are valid.
*/
public boolean validate(List<String> someStrings);
}
现在,我们有两个
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;
}
}
现在,让我们添加一个使用
IValidator
注入实例的servlet:@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();
}
}
}
当然,我们还需要在模块中将
IValidator
绑定到StrictValidator
:public class ValidatorModule implements Module {
@Override
protected void configure() {
bind(IValiator.class).to(StrictValidator.class);
}
}
但是,如果我想在一种情况下将
IValidator
有条件地绑定到StrictValidator
,而在其他情况下却将其绑定到LaissezFaireValidator
,会发生什么呢?您是否注意到上面
@LaissezFaire
上的MyServlet.validate
批注?那是一个拦截器,看起来像这样:@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 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
,当LaissezFaireInterceptor
调用StrictValidator
时,InterceptorModule
类将获得requestInjection(lfInterceptor);
的实例。相反,我希望
MyServlet
获得StrictValidator
的实例,而LaissezFaireInterceptor
获得LaissezFaireValidator
的实例。根据the Google Guice docs,当我请求注入时,可以使用命名的注释。
MyServlet
的构造函数将被修改为如下所示: @Inject
public MyServlet(@Named("strict") final IValidator validator) {
this.validator = validator;
}
并且
setValidator
的LaissezFaireInterceptor
方法将被修改为如下所示: @Inject
public void setValidator(@Named("laissezfaire") final IValidator validator) {
this.validator = validator;
}
最后,将
ValidatorModule
修改为如下所示: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);
}
}
一切都很好,只是文档特别指出要避免这种方法,因为编译器无法检查字符串名称。另外,这意味着我必须在代码中通过注入请求
@Named
的每个位置添加一个IValidator
批注,否则绑定将失败。我确实希望Provider Bindings可以为我解决这个问题,但是他们似乎对绑定的上下文一无所知。由于他们不知道请求绑定的类的类型,因此我无法选择要从
IValidator
方法返回的get()
类型。有没有更好的方法来解决此问题?
最佳答案
尽管Condit提供了一些很好的建议,我们还是选择了更直接的解决方案来解决此问题。
如上所述,我们创建了IValidator
接口以及StrictValidator
和LaissezFaireValidator
类。在默认情况下,我们使用ValidatorModule
将IValidator
绑定到StrictValidator
。提醒一下,它看起来像这样:
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
中所做的那样),我们都注入一个IValidator
实例: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) ...
}
在我们想要
LaissezFaireValidator
实例的任何地方,我们都要求注入它的具体实现来代替IValidator
: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) ...
}
这样,我们能够根据注入的上下文有条件地注入所需的实现,而无需引入任何额外的注释或工厂。
当然,它不像Guycy那样有效,但可以。