0

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);
    }
}

によるとValidatorModuleLaissezFaireInterceptorクラスはStrictValidatorwhenInterceptorModule呼び出しのインスタンスを取得しますrequestInjection(lfInterceptor);

代わりに、のインスタンスを取得し、のインスタンスをMyServlet取得したいと思います。StrictValidatorLaissezFaireInterceptorLaissezFaireValidator

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()

この問題に取り組むためのより良い方法はありますか?

4

1 に答える 1

1

Conditはいくつかの優れた提案を提供しましたが、より簡単な解決策でこの問題を解決することを選択しました。

上記のように、IValidatorインターフェースStrictValidatorLaissezFaireValidatorクラスを作成しました。デフォルトの場合、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) ...
}

このようにして、追加のアノテーションやファクトリを導入することなく、注入のコンテキストに基づいて必要な実装を条件付きで注入することができました。

確かに、それはそれができるほどギジーではありませんが、それは機能します。

于 2013-02-21T14:59:56.413 に答える