3

我有一种情况,Guice 正在为某些绑定工作,而根本不为其他绑定工作。显然我错误地使用了 API。

在某种程度上,这可能是因为我试图对我使用 Guice 的方式过于“花哨”。我已经创建了一个Modules 的继承树,我认为我已经变得太聪明(或愚蠢!)为了我自己的利益。

在查看下面的代码之前,请先了解我的意图,即提供一个可重用的东西Module,我可以将它放在 JAR 中并在多个项目中共享。这种抽象的、可重用Module的将提供所谓的“默认绑定”,任何实现Module都会自动遵守。诸如 AOP 之类的东西MethodInterceptor称为Profiler,它查找带有注释的方法@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)。您正在使用“new”来创建 DummyObj,因此无论您的模块设置多么巧妙,实例都是在 guice 之外创建的。

这是基于您的编码的一个小片段。(我使用您的校准注释,但其他所有内容都在一个类中。您应该看看“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 回答