[JAVA] Matches annotations on the interface with Spring AOP


problem

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.

Cause

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.

Solution

The annotation attached to the interface is also used by customizing ʻAspectJPointcutAdvisor` so that it is inspected at the time of pointcut judgment.

Extension of AspectJPointcutAdvisor

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));
        }
    }
}

Configuration class

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

Matches annotations on the interface with Spring AOP
gRPC on Spring Boot with grpc-spring-boot-starter
Programming with ruby (on the way)
Spring AOP for the first time
Using templates on the classpath with Apache Velocity
Come out with a suffix on the method
Publish the app made with ruby on rails
Come out with a suffix on the method 2
Determine the current page with Ruby on Rails
About Spring AOP
About spring AOP
Leverage Spring AOP + CyclicBarrier to ensure optimistic lock testing conditions on the Spring Boot app
Refactor the Decorator pattern implementation with a functional interface
Spring with Kotorin --3. Omitting curly braces from the function
Access the built-in h2db of spring boot with jdbcTemplate
Display the list in setDetails on the screen with spring-security
Challenge the settings for developing with vue.js on Rails 6
A review note of the Spring Framework Resource interface
Deploy the Spring Boot project to Tomcat on XAMPP
Control the processing flow of Spring Batch with JavaConfig.