【发布时间】:2013-02-21 22:59:56
【问题描述】:
假设我有一个名为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,会发生什么?
您注意到上面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 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,当InterceptorModule调用requestInjection(lfInterceptor);时,LaissezFaireInterceptor类会得到一个StrictValidator的实例。
相反,我希望MyServlet 获取StrictValidator 的实例,并希望LaissezFaireInterceptor 获取LaissezFaireValidator 的实例。
根据the Google Guice docs,我可以在请求注入时使用命名注释。 MyServlet 的构造函数将被修改为如下所示:
@Inject
public MyServlet(@Named("strict") final IValidator validator) {
this.validator = validator;
}
LaissezFaireInterceptor 的 setValidator 方法将被修改为如下所示:
@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);
}
}
这一切都很好,除了文档特别说要避免这种方法,因为编译器无法检查字符串名称。另外,这意味着我必须在代码中通过注入请求IValidator的每个地方添加@Named注解,否则绑定会失败。
我真的希望Provider Bindings 可以为我解决这个问题,但他们似乎对进行绑定的上下文一无所知。由于他们不知道请求绑定的类的类型,所以我无法选择从get() 方法返回的IValidator 的类型。
有没有更好的方法来解决这个问题?
【问题讨论】:
-
实际的绑定注释怎么样:
@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME) public @interface Strict{}?你也可以试试assisted inject -
@condit 绑定注释比我提到的@Named 注释更干净,因为它可以被编译器检查,但它仍然需要我注释我请求注入@987654362 的每个地方@instance,所以这不是一个巨大的改进。
-
带有类型侦听器的custom injection 怎么样?