Reputation: 6371
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
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