Guice won't intercept annotated methods

dependency-injection, guice, java

Solution

Interception only works on Objects created by Guice (see "Limitations" http://code.google.com/p/google-guice/wiki/AOP#Limitations). You are using "new" to create the DummyObj, so no matter how clever your Module is Set up, the instance is created Outside guice.

Here's a little snipplet based on your coding. (I use your Calibrated Annotation, but had everything else in one class. You should have a look at "AbstractModule". It saves a lot of manual stuff you did with your Module-Hierarchy.

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

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

So you see? I never use the word "new" (except for the Module ....). I let Guice handle the Dummy-Object and just configure the value for the nonsense int, which is then injected.

Output:

Intercepted@invoke!
I have been intercepted!
29

Problem

I have a situation where Guice is working for some bindings, and not at all for others. Clearly I am using the API incorrectly. In part, it's probably because I'm trying to get too "fancy" with how I'm using Guice. I've created an inheritance tree of `Module`s and I think I've gotten too clever (or foolish!) for my own good. Before you look at the code below, just please understand my intention, which was to provide a reusable `Module` that I can place in a JAR and share across multiple projects. This abstract, reusable `Module` would provide so-called "default bindings" that any implementing `Module` would automatically honor. Things like an AOP `MethodInterceptor` called `Profiler`, which looks for methods annotated with `@Calibrated` and automatically logs how long it took for that method to execute, etc. Observe the following: ``` @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); } } ``` So, to use this small library: ``` 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()); } } ``` This is a lot of code so I may have introduced a few typos when keying it all in, but I assure you this code compiles and throws no exceptions when ran. Here's what happens: - The `@Calibrated shouldBeIntercepted()` method is not intercepted; but... - The console output shows the dummy's nonsense integer as...29!!!! So, regardless of how poor a design you may think this is, you can't argue that Guice is indeed working for 1 binding (the instance binding), but not the AOP method interception. If the instance binding wasn't working, then I would happily revisit my design. But something else is going on here. I'm wondering if my inheritance tree is throwing the `Binder` off somehow? And I've verified that I am binding the interceptor to the annotation correctly: I created another package where I just implement `Module` (instead of this inheritance tree) and use the same annotation, the same `Profiler`, and it works perfectly fine. I've used `Injector.getAllBindings()` to print out the map of all my `MyAppModule`'s bindings as Strings. Nothing is cropping up as the clear source of this bug.

Original source