Affects: spring-aop:5.0.9.RELEASE, spring-boot-starter-aop:2.0.5.RELEASE
public interface FooService {
@MyAnnotation
void hello();
}
public class FooServiceImpl implements FooService {
void hello();
}
public class MyAspect {
@Pointcut("@annotation(MyAnnotation")
public void myPointcut() {
}
@Around("myPointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
...
}
}
@Configuration
public class MyAopConfiguration {
@Bean
public MyAspect myAspect () {
return new MyAspect();
}
}
Using spring-aop
like the code above, it doesn't work, around(ProceedingJoinPoint pjp)
never executes.
If put the @MyAnnotation
on the implemented method of FooServiceImpl
, it works.
public interface FooService {
void hello();
}
public class FooServiceImpl implements FooService {
@MyAnnotation
void hello();
}
Learned from two questions in Stack Overflow:
- https://stackoverflow.com/questions/6594457/aspectj-annotation-pointcut-not-triggered-for-interface-annotations
- https://stackoverflow.com/questions/7178782/aspectj-pointcut-for-methods-that-override-an-interface-method-with-an-annotati
According to the Java 5 specification, non-type annotations are not inherited, and annotations on types are only inherited if they have the @Inherited
meta-annotation.
It's seems that it's impossible in spring-aop
.
I tried the code without spring-aop
, only using aspectjweaver
1.7.4, and adding an aop.xml
in resources/META-INF
:
<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
<aspects>
<aspect name="com.xxx.MyAspect"/>
</aspects>
<weaver options="-verbose -showWeaveInfo -Xset:weaveJavaxPackages=true" />
</aspectj>
and adding JVM parameter:
-javaagent:xxx/aspectjweaver-1.7.4.jar
It works no matter the @MyAnnotation
is on the interface or class. And then in around(ProceedingJoinPoint pjp)
, I can get the @MyAnnotation
with the API of pjp, like this:
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Class<?> targetClass = pjp.getTarget().getClass();
Method method = targetClass.getDeclaredMethod(signature.getName(), signature.getMethod().getParameterTypes());
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
or
Class<?>[] interfaces = targetClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
Method method = targetClass.getDeclaredMethod(signature.getName(),
signature.getMethod().getParameterTypes());
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
}
So I think spring-aop
may also have way to solve this problem, since it's a common use case for users to add annotation on the method of interface.
Something in AopUtils#getMostSpecificMethod
, AspectJExpressionPointcut#getShadowMatch(Method targetMethod, Method originalMethod)
may be about, I'm not sure.
Could you please give some help?
Comment From: jhoeller
I'm afraid there is not much we can do about this on Spring's side since those annotation checks are performed by AspectJ itself which focuses on the concrete class to be weaved and generally follows Java language semantics where interface-declared annotations are not inherited.
Comment From: jhoeller
Reopening again after re-reading the details and noticing that the AspectJ compiler does seem to behave differently there. We should investigate how it does that, maybe we can imitate that via custom ShadowMatch
checks indeed.
Comment From: constantinLu
Any advancement on this ?
Comment From: FiruzzZ
it would be amazing to be able to listen to annotated methods from interfaces'
Comment From: NicoStrecker
Also in need
Comment From: ReXtrem
In need aswell!
@Around("@annotation(myAnnotation)")
public Object aroundMyAnnotation(
ProceedingJoinPoint proceedingJoinPoint, myAnnotation annotation) throws Throwable {
...
}