“This is the fourth day of my participation in the First Challenge 2022. For details: First Challenge 2022”

Spring AOP has many applications in Spring projects, such as @enableAsync and @async are AOP embodiment, so how can we customize our own annotations under the principle of Spring AOP?

1. Custom annotation principle based on Spring AOP

Spring AOP is implemented based on dynamic proxies. By default, dynamic proxies provided by the JDK are used if interfaces are used, and CGLIB is used if classes are used. Turn on AOP (along with AspectJ support) with @enableAspectJAutoProxy.

  • @enableAspectJAutoProxy Starts Spring AOP

  • Spring AOP comes in two implementations:

    • Based on AspectJ annotations
    • Based on Spring AOP ideas, namely Advice, Pointcut, and Advisor, Corresponding MethodInterceptor, AbstractBeanFactoryPointcutAdvisor, StaticMethodMatcherPointcut three classes.
  • The corresponding proxy class is generated after the Spring container is started, and the corresponding method is executed according to the slice when the method is executed

2. Code practice

The combination of @enable type annotations with the AOP extended interface completes the functionality.

2.1 Define the @enable annotation

Combine the @enablelog functionality from the previous article “How to Customize Spring Enable Annotations”

2.2 Define section annotations

Define an @log annotation for logging

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Log {
​
    /**
     * log template
     * @return
     */
    String template() default "";
    
}
Copy the code

2.3 Implement the three interfaces of AOP

MethodInterceptor is a MethodInterceptor for dynamic classes that intercepts executing methods. Here are the AOP dynamic class enhancements

public class LogAdvice implements MethodInterceptor, @override public Object Invoke (MethodInvocation) throws Throwable {Method MethodInvocation method = invocation.getMethod(); return execute(invocation, invocation.getThis(), method, invocation.getArguments()); } private Object execute(MethodInvocation invoker, Object target, Method method, Object[] args) throws Throwable { try { LogWorker worker = new LogWorker(target, method, args); if (this.async) { logExecutor.submit(worker); } else { worker.run(); } } catch (Exception e) { //e.printStackTrace(); logger.warn("Failure to record logs!" , e); } return invoker.proceed(); }}Copy the code

The main implementation is the Invoke method, where the logging method is implemented. And the return execution result of the call target class.

StaticMethodMatcherPointcut breakthrough point of the implementation class:

public class LogPointcut extends StaticMethodMatcherPointcut{
​
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return AnnotatedElementUtils.hasAnnotation(method, Log.class);
    }
}
Copy the code

This is where the pointcut is determined, and the code says that when a method is annotated @log it matches the pointcut

To organize the Advice and pointcuts AbstractBeanFactoryPointcutAdvisor: notice is used

public class LogAdvisor extends AbstractBeanFactoryPointcutAdvisor{
​
    private Pointcut logPointcut;
​
    public LogAdvisor(Pointcut logPointcut) {
        this.logPointcut = logPointcut;
    }
​
    /**
     * Get the Pointcut that drives this advisor.
     */
    @Override
    public Pointcut getPointcut() {
        return this.logPointcut;
    }
}
Copy the code

These three interfaces are implemented to organize Advice, Pointcut, and Advisor, and then register with the Spring IOC container.

2.4 Use facet annotations in required methods (classes)

@SpringBootApplication @EnableLog public class Application { public static void main(String[] args) { SpringApplication.run(Application.class,args); } } @Component public class Test { public void test(){ System.out.println(1111); } @log (template = "user ${#user.name} info: ${@test.getName(#user)}") public Boolean addUser(user user){return true; } public String getName(User user){ return user.getName(); }}Copy the code

First add @enablelog to the SpringBoot boot class and then add @log to the methods of the class you want to Log

AOP annotation definition code: github.com/mxsm/mxsm-l…

Test code: github.com/mxsm/spring…

Here is the implementation of custom interface functionality based on the Spring AOP interface, and in Spring can be implemented according to AspectJ annotations. I’m not going to go into detail here. AOP Aspect Oriented Programming with Spring So there are two ways to define Spring AOP-based annotations.

3. Summary

  • Aspectj-based definitions are relatively simple to implement and do not need to be used with the custom @enableXXX annotation. You just need to enable AOP with @enableAspectJAutoProxy (complete with our custom @enablexxx annotation).
  • The underlying definition of annotations based on Spring AOP is based on dynamic proxies. The Spring dynamic proxy can be JDK or Cglib