2012-05-22 47 views
2

我有一种情况,Guice正在为某些绑定工作,而不是为其他人工作。很明显,我错误地使用了API。Guice不会拦截带注释的方法

部分原因可能是因为我试图让我对Guice的使用过于“迷恋”。我创建了一个Module的继承树,我认为我为了自己的利益已经太聪明了(或愚蠢!)。

你看看下面的代码之前,就请理解我的用意,这是提供一个可重用的Module,我可以在一个JAR放置并在多个项目共享。这个抽象的,可重用的Module将提供所谓的“默认绑定”,任何实现的Module都会自动颁发。事情是这样叫Profiler的AOP MethodInterceptor,它看起来与@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 !!!!

所以,无论多么贫穷设计你可能认为这是,你不能说吉斯确实是1结合工作(实例绑定),而不是AOP方法拦截。

如果实例绑定不起作用,那么我会很高兴地重新访问我的设计。但其他事情正在发生。我想知道我的继承树是否抛弃了Binder

而且我已经验证了我正确地绑定拦截器的注解:我创建了另一个包,我只是执行(而不是这个继承树)Module并使用相同的注解,同样Profiler,并且它完美精细。

我用Injector.getAllBindings()打印出我的所有MyAppModule的绑定作为字符串的地图。没有任何东西可以作为这个错误的明确来源。

任何想法?

回答

6

拦截仅适用于由Guice创建的对象(请参阅“限制”http://code.google.com/p/google-guice/wiki/AOP#Limitations)。您正在使用“new”创建DummyObj,因此无论您的模块设置得有多聪明,该实例都会在Guice外创建。

这里有一个基于你的编码的小snipplet。 (我使用校准注释,但是其他所有内容都在一个类中,您应该查看“AbstractModule”。它可以节省您在模块层次结构中执行的大量手动操作。

public class MyModule extends AbstractModule implements MethodInterceptor { 

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

    System.out.println("[email protected]!"); 

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

所以,你看到了什么?我从来没有使用“新”这个词(Module除外)。我让Guice处理Dummy-Object,然后为无意义的int配置值,然后将其注入。

输出:

[email protected]! 
I have been intercepted! 
29 
+0

所以是你在说什么:(1)因为我使用'new',我实际上没有获得通过吉斯的'Dummy';和(2)因为我然后从非Guice注入的'Dummy'运行'Calibrated'方法,那Guice没有拦截它?这是主意吗?如果是这样,那就很有道理。如果不是,请你澄清一下吗?再次感谢您的帮助和这么好的回答(+1)! – IAmYourFaja

+0

您的DummyObj实例不是由guice创建的。它由你创建并且在模块中绑定,所以你总是会得到相同的预创建的实例。所以基本上:是的,我说的是(1)和(2)。它的理解有点棘手,请阅读下面的链接(上面的链接)。不要问我为什么这样做......这是一个很好的最佳实践,尽可能避免“新”。 –