In Spring AOP (AspectJ), pointcuts that use annotations attached to interfaces as markers cannot be used. Therefore, it is not possible to insert an interceptor as an annotation mark into the method of Mapper interface of MyBatis.
Annotations attached to methods are not inherited by methods of inherited class (or class that implements interface). And since Spring AOP (or AspectJ) targets instantiated beans and their types are classes that implement the interface, annotations attached to interface methods are not inspected.
The annotation attached to the interface is also used by customizing ʻAspectJPointcutAdvisor` so that it is inspected at the time of pointcut judgment.
Since there is a method to get the pointcut, override this and also check the annotations on the interface.
InterfaceMethodAwareAspectJPointcutAdvisor.java
package aop;
import java.lang.reflect.Method;
import java.util.stream.Stream;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.aspectj.AbstractAspectJAdvice;
import org.springframework.aop.aspectj.AspectJPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.val;
public class InterfaceMethodAwareAspectJPointcutAdvisor extends AspectJPointcutAdvisor {
public InterfaceMethodAwareAspectJPointcutAdvisor(@NonNull AbstractAspectJAdvice advice) {
super(advice);
}
@Override
public Pointcut getPointcut() {
val original = super.getPointcut();
return new ComposablePointcut(original)
.union(new InterfaceAwareMethodMatcher(original.getMethodMatcher()));
}
@RequiredArgsConstructor
private static class InterfaceAwareMethodMatcher implements MethodMatcher {
@NonNull
private final MethodMatcher underlying;
@Override
public boolean isRuntime() {
return underlying.isRuntime();
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
return Stream.of(targetClass.getInterfaces())
.anyMatch(x -> underlying.matches(method, x));
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return Stream.of(targetClass.getInterfaces())
.anyMatch(x -> underlying.matches(method, x, args));
}
}
}
Register the Advisor using ʻInterfaceMethodAwareAspectJPointcutAdvisor`, which also checks the interface annotations created above.
AspectConfiguration.java
@Configuration
@EnableAspectJAutoProxy
public class AspectConfiguraiton implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(@NonNull ConfigurableListableBeanFactory factory) {
val original = new FooAspect();
val advisor = new ReflectiveAspectJAdvisorFactory()
.getAdvisors(new SingletonMetadataAwareAspectInstanceFactory(original, "fooAspect"));
advisors.stream()
.map(advisor -> new InterfaceMethodAwareAspectJPointcutAdivsor((AbstractAspectJAdvice) advisor))
.forEacth(advisor -> factory.registerSingleton(resolveName(advisor), advisor));
}
private static String resolveName(AspectJPointcutAdvisor advisor) {
val info = (AspectJPrecedenceInformation) advisor.getAdvice();
return info.getAspectName() + '#' + info.getDeclarationOrder();
}
}
Recommended Posts