Implementation of spring boot custom annotation (used with AOP, reflection mechanism and interceptor)

Posted by Harbinger on Mon, 21 Feb 2022 04:50:05 +0100

1. Annotation type

Java annotation is used to provide metadata for Java code. You can obtain the specified annotation object through reflection, and then obtain the metadata information in the annotation.

Java annotations have four standard meta annotations: @ Target, @ Retention, @ Documented, @ Inherited.

1.1 @Target

@Target describes the object range modified by Annotation, which is commonly used as follows:

  • @Target(ElementType.TYPE): function interface, class, enumeration and annotation

  • @Target(ElementType.FIELD): constant used for attribute fields and enumerations

  • @Target(ElementType.METHOD): action method

  • @Target(ElementType.PARAMETER): action method parameter

1.2 @Retention

Retention defines the length of time the Annotation is retained, indicating the level at which Annotation information needs to be saved for tracing

The life cycle of the described annotation (i.e. to what extent the described annotation is valid)

  • @The Retention(RetentionPolicy.SOURCE) annotation only exists in the source code and is not included in the class bytecode file

  • @Retention(RetentionPolicy.CLASS) is the default policy, which exists in the class bytecode file, but cannot be obtained at runtime

  • @Retention(RetentionPolicy.RUNTIME) exists in the class bytecode file and can be obtained through reflection at runtime

In development, the user-defined annotation is used during the program running, so @ Retention(RetentionPolicy.RUNTIME) is used by default

1.3 @Documented

@Documented is used to include elements in annotations into Javadoc.

1.4 @Inherited

@Inherited meta annotation is a tag annotation, @ inherited describes that a marked type is inherited.

If the annotation with @ Inherited meta annotation modifies a class and its subclass is not modified by other annotations, its subclass will inherit the annotation of the parent class.

2. Custom annotation implementation

  • Custom annotations can be used with AOP, such as logging.
  • User defined annotations can be directly used in a method in conjunction with the reflection mechanism.
  • The user-defined annotation can be used with the interceptor. Whether to verify the token.

2.1 creating annotations

Create custom annotation: SysLog

Annotations can have no attribute fields

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {

    /**
     * Module
     */
    String model() default "";

    /**
     * Interface name
     */
    String name() default "";

    /**
     * Function code
     *
     * @return
     */
    String[] code() default "";
}

2.2 AOP implementation

Join the AOP class SysLogAspect and use the custom annotation SysLog as the entry point, as follows:

@Aspect
@Component
public class SysLogAspect {
    /**
     * Filter class
     * Specify custom annotations as pointcuts
     **/
    @Pointcut("@annotation(com.lhz.mail.test.SysLog)")
    public void logPoint() {
    }

    /**
     * Surround the notification and call the target method
     */
    @Around("logPoint()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Signature signature = proceedingJoinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            SysLog sysLog = method.getAnnotation(SysLog.class);
            // Gets the attribute value of the annotation
            String[] code = sysLog.code();
            String name = sysLog.name();
            String model = sysLog.model();
            System.out.println("code: " + Arrays.toString(code));
            System.out.println("name: " + name);
            System.out.println("model: " + model);
        }
        return proceedingJoinPoint.proceed();
    }

}

2.3 reflection realization

You can directly use the reflection mechanism to judge whether there are annotations for a class, method and field, as follows:

    public static void main(String[] args) {
        // Determine whether @ SysLog exists on the Person class:
        Class<Person> clazz = Person.class;
        boolean isAnnotation = clazz.isAnnotationPresent(SysLog.class);
        if (isAnnotation) {
            SysLog sysLog = clazz.getAnnotation((SysLog.class));
            String model = sysLog.model();
            String name = sysLog.name();
        }
    }
    
    public static void main2(String[] args) {
        // Determine whether @ SysLog exists on the method of Person class:
        Class<Person> clazz = Person.class;
        // method
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            boolean isAnnotation = method.isAnnotationPresent(SysLog.class);
            if (isAnnotation) {
                System.out.println("method:" + method.getName() + "There are custom annotations");
                SysLog sysLog = method.getAnnotation((SysLog.class));
                String model = sysLog.model();
                String name = sysLog.name();
            }
        }
    }

    public static void main3(String[] args) {
        // Judge whether @ SysLog exists in the field of Person class:
        Class<Person> clazz = Person.class;
        // method
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            boolean isAnnotation = field.isAnnotationPresent(SysLog.class);
            if (isAnnotation) {
                System.out.println("Field:" + field.getName() + "Custom annotation exists");
                SysLog sysLog = field.getAnnotation((SysLog.class));
                String model = sysLog.model();
                String name = sysLog.name();
            }
        }
    }

2.4 interceptor implementation

Cooperate with the interceptor to determine whether a request does not need token verification. The logic is as follows:

1. Create annotation IgnoreToken

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreToken {
}

2. Controller load annotation:

    @IgnoreToken
    @GetMapping("test")
    public Object test() {
    	return object;
    }

3. Interceptor judgment comments:

@Component
public class AuthenticationHandlerInterceptor implements HandlerInterceptor {
@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("Entry interceptor,URL:{}", request.getServletPath());
        
        Method method = ((HandlerMethod) handler).getMethod();
        // Judge whether the method requesting the interface has the IgnoreToken annotation
        if (method.isAnnotationPresent(IgnoreToken.class)) {
            return true;
        }else{
          // token verification logic
          ...
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

Topics: Java Spring Spring Boot