3

Guiceが一部のバインディングで機能していて、他のバインディングではまったく機能していないという状況があります。明らかに、私はAPIを誤って使用しています。

部分的には、Guiceの使い方に「夢中」になりすぎたことが原因かもしれません。私はModulesの継承ツリーを作成しましたが、自分の利益のために賢くなりすぎた(または愚かだった!)と思います。

以下のコードを見る前に、JARに配置して複数のプロジェクト間で共有できる再利用可能なものを提供するという、私の意図を理解してください。Moduleこの抽象的で再利用可能なものは、実装が自動的に尊重Moduleする、いわゆる「デフォルトのバインディング」を提供します。と呼ばれるModuleAOPのようなもので、注釈が付けられたメソッドを探し、そのメソッドの実行にかかった時間を自動的に記録します。MethodInterceptorProfiler@Calibrated

次の点に注意してください。

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

public class Profiler implement MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation arg0) throws Throwable {
        // Eventually it will calculate and log the amount of time
        // it takes an intercepted method to execute, hence "Profiler".
        System.out.println("Intercepted!");
        return arg0.proceed();
    }
}

public abstract class BaseModule implements Module {
    private Binder binder;

    public BaseModule() {
        super();
    }

    public abstract void bindDependencies();

    @Override
    public void configure(Binder bind) {
        // Save the binder Guice passes so our subclasses can reuse it.
        setBinder(bind);

        // TODO: For now do nothing, down the road add some
        // "default bindings" here.

        // Now let subclasses define their own bindings.
        bindDependencies();
    }

    // getter and setter for "binder" field
    // ...
}

public abstract class AbstractAppModule extends BaseModule {
    /* Guice Injector. */
    private Injector injector;

    // Some other fields (unimportant for this code snippet)

    public AbstractAppModule() {
        super();
    }

    // getters and setters for all fields
    // ...

    public Object inject(Class<?> classKey) {
        if(injector == null)
            injector = Guice.createInjector(this);

        return injector.getInstance(classKey);
    }
}

したがって、この小さなライブラリを使用するには、次のようにします。

public class DummyObj {
    private int nonsenseInteger = -1;

    // getter & setter for nonsenseInteger

    @Calibrated
   public void shouldBeIntercepted() {
       System.out.println("I have been intercepted.");
   }
}

public class MyAppModule extends AbstractAppModule {
    private Profiler profiler;

    // getter and setter for profiler

    @Override
    public void bindDependencies() {
        DummyObj dummy = new DummyObj();
        dummy.setNonsenseInteger(29);

        // When asked for a DummyObj.class, return this instance.
        getBinder().bind(DummyObj.class).toInstance(dummy);

        // When a method is @Calibrated, intercept it with the given profiler.
        getBinder().bindInterceptor(Matchers.any(),
            Matchers.annotatedWith(Calibrated.class),
            profiler);
    }
}

public class TestGuice {
    public static void main(String[] args) {
        Profiler profiler = new Profiler();
        MyAppModule mod = new MyAppModule();
        mod.setProfiler(profiler);

        // Should return the bounded instance.
        DummyObj dummy = (DummyObj.class)mod.inject(DummyObj.class);

        // Should be intercepted...
        dummy.shouldBeIntercepted();

        System.out.println(dummy.getNonsenseInteger());
    }
}

これは多くのコードであるため、すべてを入力するときにいくつかのタイプミスを導入した可能性がありますが、このコードはコンパイルされ、実行時に例外がスローされないことを保証します。

何が起こるかです:

  • @Calibrated shouldBeIntercepted()メソッドは傍受されません。しかし...
  • コンソール出力には、ダミーのナンセンス整数が...29!!!!と表示されます。

したがって、これがどれほど貧弱な設計であると思われるかにかかわらず、Guiceが実際に1つのバインディング(インスタンスバインディング)で機能していると主張することはできませんが、AOPメソッドのインターセプトでは機能していません。

インスタンスバインディングが機能していなかった場合は、喜んで自分のデザインを再検討します。しかし、ここでは別のことが起こっています。私の継承ツリーがBinderどういうわけか捨てられているのだろうか?

そして、インターセプターをアノテーションに正しくバインドしていることを確認しました。Module(この継承ツリーの代わりに)実装し、同じアノテーションを使用する別のパッケージを作成しましProfilerた。これは完全に正常に機能します。

私は以前、すべてのバインディングのInjector.getAllBindings()マップを文字列として出力していました。MyAppModuleこのバグの明確な原因として何も現れていません。

何か案は?

4

1 に答える 1

7

傍受は、Guice によって作成されたオブジェクトに対してのみ機能します (「制限」http://code.google.com/p/google-guice/wiki/AOP#Limitationsを参照)。DummyObj を作成するために「new」を使用しているため、モジュールがどれほど巧妙に設定されていても、インスタンスは外部で作成されます。

これは、コーディングに基づく小さなスニペットです。(私は校正済み注釈を使用しますが、他のすべてを 1 つのクラスにまとめました。「AbstractModule」を確認する必要があります。これにより、モジュール階層で行った多くの手作業を節約できます。

public class MyModule extends AbstractModule implements MethodInterceptor {

@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {

    System.out.println("Intercepted@invoke!");

    return methodInvocation.proceed();
}

@Override
protected void configure() {
    bind(Integer.class).annotatedWith(Names.named("nonsense")).toInstance(29);
    bindInterceptor(Matchers.any(), Matchers.annotatedWith(Calibrated.class), this);
}

public static void main(String... args) {
    Dummy dummy = Guice.createInjector(new MyModule()).getInstance(Dummy.class);

    dummy.doSomething();

    System.out.println(dummy.getNonsense());
}
}

そして私のダミー:

public class Dummy {

@Inject
@Named("nonsense")
private int nonsense;


public int getNonsense() {
    return nonsense;
}


public void setNonsense(int nonsense) {
    this.nonsense = nonsense;
}

@Calibrated
public void doSomething() {
    System.out.println("I have been intercepted!");
}
}

ご覧のように?私は「新しい」という言葉を決して使いません (モジュール .... を除いて)。Guice に Dummy-Object を処理させ、ナンセンス int の値を構成するだけで、それが注入されます。

出力:

Intercepted@invoke!
I have been intercepted!
29
于 2012-05-23T06:01:43.887 に答える