Judking
Judking

Reputation: 6371

Why can't the aop execute when a method is called from anonymous class' method?

Here is my custom annotation AnnoLogExecTime and class AOP:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AnnoLogExecTime {

}

@Aspect
@Service
public class AOP {
    Logger logger = LoggerFactory.getLogger(AOP.class);

    @Around("execution(@com.judking.general.aop.AnnoLogExecTime * *(..))")
    public Object calExecTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable        {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();

        long t1 = System.currentTimeMillis();
        Object obj = proceedingJoinPoint.proceed();
        long t2 = System.currentTimeMillis();

        logger.info("method `"+method.getName()+"` takes "+(t2-t1)+"ms");
        return obj;
    }
}

And the test case is as below:

@Service
class A    {
  public void go()  {
    B b = new B()  {    //Anonymous class B
      @Override
      public void exec()    {
        aopMethod();
      }
    };
    b.exec();
  }

  @AnnoLogExecTime
  public void aopMethod()    {
    System.out.println("aopMethod");
  }
}

@Service
class B    {
  public void exec()    {
    System.out.println("exec");
  }
}

When I call a.aopMethod(), the AOP.calExecTime is hooked up to a.aopMethod().

But if I call a.go(), which is using anonymous class B instance to call a.aopMethod(), then the AOP.calExecTime is NOT hooked up to a.aopMethod().

Could anyone give me an explanation to this phenomenon? And please give me a way to resolve this problem in the case of anonymous class. Thanks a lot!

Upvotes: 1

Views: 629

Answers (1)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279920

This is not exactly because it is an anonymous inner class. What you are experiencing is a limitation of AOP proxies.

When you have

A a = ...; // get proxy

The proxy itself wraps the actual instance in a wrapper instance. When you interact with this wrapper instance by calling

a.aopMethod();

the proxy interceptor intercepts the call and can execute the advice.

This would apply to you calling

a.go()

if there was a joinpoint. Instead nothing intercepts that call, and the call to go() goes through the interceptor and the method is called on the actual instance

actualA.go();

When you create the anonymous inner class and have

@Override
public void exec()    {
    aopMethod();
}

it's implicitly doing

@Override
public void exec()    {
    A.this.aopMethod();
}

which goes around the proxy because you are calling it on the actual instance, not the wrapper.

You might not be using Spring to generate your proxies, but their documentation explains this pretty well.

Upvotes: 1

Related Questions