spring-framework
spring-framework copied to clipboard
Support annotation on the method of interface in spring-aop integrate with aspectj
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 execute.
If put the @MyAnnotation
on the implement method of FooServiceImpl
, it works.
public interface FooService {
void hello();
}
public class FooServiceImpl implements FooService {
@MyAnnotation
void hello();
}
Learned from two questions in stackoverflow: 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 unpossible in spring-aop.
I tried the code without spring-aop, only using aspectjweaver 1.7.4, and add a aop.xml in the 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 add 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 scene 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?
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.
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.
Any advancement on this ?
it would be amazing to be able to listen to annotated methods from interfaces'
Also in need