Guice不能拦截标注方法

3
我有这样一个情况,Guice对于某些绑定有效,而对于其他绑定则完全无效。显然我在错误地使用API。
部分原因可能是因为我试图用Guice的方式变得太“花哨”了。我创建了一个Module的继承树,我认为我已经变得太聪明(或愚蠢了!)。
在查看下面的代码之前,请理解我的意图,我的意图是提供可重用的Module,我可以将其放置在JAR中,并在多个项目之间共享。这个抽象的、可重用的Module会提供所谓的“默认绑定”,任何实施Module都会自动遵守这些绑定。例如,一个名为Profiler的AOPMethodInterceptor,它查找带有@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绑定的映射字符串。没有任何东西浮现出来作为这个错误的明显来源。

1个回答

9
拦截只能在Guice创建的对象上工作(详见“限制”http://code.google.com/p/google-guice/wiki/AOP#Limitations)。您正在使用“new”创建DummyObj,因此无论您的Module设置多么聪明,该实例都是在guice之外创建的。
以下是基于您的代码的小片段。 (我使用您的Calibrated注释,但其他所有内容都在一个类中。您应该查看“AbstractModule”。它可以节省大量手动操作您在Module层次结构中进行的操作。)
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());
}
}

还有我的Dummy:

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

所以你明白了吗?除了模块之外,我从未使用“new”这个词。我让Guice处理虚拟对象并仅配置无意义 int 的值,然后注入该值。

输出:

Intercepted@invoke!
I have been intercepted!
29

那么您的意思是:(1) 由于我正在使用 new,因此我实际上并未通过 Guice 获取 Dummy;(2) 由于我随后从非 Guice 注入的 Dummy 运行了一个 Calibrated 方法,因此 Guice 无法截取它? 如果是这样的话,那就很有道理。 如果不是,请明确一下。 再次感谢您的帮助和出色的答案 (+1)! - IAmYourFaja
你的DummyObj实例不是由guice创建的。它是由你自己创建并在模块中绑定的,因此你总是会得到相同的预先创建的实例。 基本上:是的,我是在说(1)和(2)。这有点棘手,要理解,请阅读上面的guice-aop网站(链接)。别问我为什么必须这样做……在我看来,尽可能避免使用“new”是一个很好的最佳实践。 - Jan Galinski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接