IValidator
次のようなインターフェイスがあるとしましょう。
public interface IValidator {
/**
* Returns true if the specified strings are valid.
*/
public boolean validate(List<String> someStrings);
}
ここで、次の2つの実装があるとしましょう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
。
@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
whenInterceptorModule
呼び出しのインスタンスを取得しますrequestInjection(lfInterceptor);
。
代わりに、のインスタンスを取得し、のインスタンスをMyServlet
取得したいと思います。StrictValidator
LaissezFaireInterceptor
LaissezFaireValidator
Google Guiceのドキュメントによると、インジェクションをリクエストするときに名前付きアノテーションを使用できます。のコンストラクタは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
ます。そうしないと、バインディングが失敗します。
プロバイダーバインディングがこの問題を解決できることを本当に望んでいましたが、バインディングが作成されているコンテキストについては何も知らないようです。バインディングを要求しているクラスのタイプがわからないため、メソッドIValidator
から返すタイプを選択できません。get()
この問題に取り組むためのより良い方法はありますか?