Ref

  • JAVA custom annotations | Segmentfault
  • Java custom annotations and usage scenarios | the Denver nuggets
  • SpringBoot custom annotations + AOP print logs | Blog

What are annotations

Annotations, introduced in version 5.0 of Java SE, are descriptions of Java source code and are metadata (data that describes data).

  • The definition of annotations passes@interfaceAll annotations are automatically inheritedjava.lang.AnnotatioN Interface, and cannot inherit from other classes or interfaces.
  • Annotated member parameters can only be usedpublicOr the default (defaultAccess modifier to modify.
  • Member arguments can only use the eight basic types (byte, Short, CHAR, int, Long, float, double, Boolean) and data types (String, Enum, Class, Annotations, etc.), and their arrays.
  • To get Annotation information for class methods and fields, you can only get Annotation objects using Java’s reflection technology.
  • Annotations can have no defined members, just identifiers.

Annotations classification

Annotations can be divided into three categories by source

  1. The JDK annotations
  2. Third Party notes
  3. Custom annotations

The JDK annotations

JAVA built-in annotations are in java.lang and the four meta-annotations are in java.lang.annotation.

JAVA built-in annotations

  • @override (mark Override method)
  • @deprecated (mark obsolete)
  • @SuppressWarnings (Ignore warnings)

Meta-notes (notes of notes)

  • @target (The Target of the annotation)
  • @Retention (Life Cycle of Annotations)
  • @document (whether annotations are included in JavaDoc)
  • @Inherited (Whether child classes are allowed to integrate this annotation)
@Target

The @target annotation indicates the type of JAVA element to which the annotation can be applied.

The Target type describe
ElementType.TYPE Applies to classes, interfaces (including annotation types), enumerations
ElementType.FIELD Applied to attributes (including constants in enumerations)
ElementType.METHOD Applied to methods
ElementType.PARAMETER Parameter applied to a method
ElementType.CONSTRUCTOR Apply to the constructor
ElementType.LOCAL_VARIABLE Apply to local variables
ElementType.ANNOTATION_TYPE Applies to annotation types
ElementType.PACKAGE Applied to the package
ElementType.TYPE_PARAMETER Added in version 1.8 for type variables
ElementType.TYPE_USE New in version 1.8. Applies to any statement that uses a type (for example, in declarations, generics, and casts)
@Retention

@Retention indicates the lifetime of the annotation.

Life cycle type describe
RetentionPolicy.SOURCE Discarded at compile time and not included in the class file
RetentionPolicy.CLASS The JVM is discarded when loaded and is included in the class file, default
RetentionPolicy.RUNTIME It is never discarded, and you can use reflection to get information about the annotation. Loaded by the JVM, contained in a class file, available at run time. The most common way to use custom annotations.
@Document

The element that indicates the annotation tag can be documented by Javadoc or a similar tool

@Inherited

An annotation that indicates that the @Inherited annotation is used, and that child classes of the labeled class also have this annotation

Third Party notes

Such as annotations for various frameworks, such as Spring annotations

  • @Autowired
  • @Service
  • .

Custom annotations

Use meta-annotations to define your own annotations, as described below.

Annotation syntax

The annotation syntax is as follows.

/** * modifier@interfaceAnnotation name {* Annotation element declaration 1 * Annotation element declaration 2 *} * modifiers: Access modifiers must be public and default to pubic; * Keyword: Must be@interface; * Annotation name: Annotation name is the name of the custom annotation, which will be used when it is used. * Annotation type element: The annotation type element is the content of the annotation and can be understood as the implementation part of the custom interface; * /
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyTestAnnotation {
    /**
     *    注解的元素声明的两种形式
     *    type elementName();
     *    type elementName() default value;  
     */
    String value(a) default "test";
}
Copy the code

Here is a Demo of the @service annotation with the annotation syntax.

@Target({ElementType.TYPE})// elementtype. TYPE stands for use on annotations
@Retention(RetentionPolicy.RUNTIME)// RetentionPolicy.RUNTIME stands for RUNTIME use and can be retrieved by reflection
@Documented// included in JavaDoc
@Component// Allow automatic detection through packet scanning
public @interface Service {

    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     * @return the suggested component name, if any (or empty String otherwise)
     */
    @AliasFor(annotation = Component.class)
    String value(a) default "";
}
Copy the code

Implement custom annotations

Here is a Demo that implements custom annotations in three steps

  1. Define custom annotations
  2. Configuration annotations
  3. Parse annotations using reflection

Define custom annotations

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyTestAnnotation {
    String value(a) default "test";
}
Copy the code

Configuration annotations

@Data
@Builder
@MyTestAnnotation
public class MyBean {
    private String name;
    private int age;
}
Copy the code

Parse annotations using reflection

public class MyTest {

    //isAnnotationPresent: determines whether the current element is annotated
    //getAnnotation: Returns the specified annotation
    //getAnnotations: Returns all annotations
    public static void main(String[] args) {
        try {
            // Get the Class object of MyBean
            MyBean myBean = MyBean.builder().build();
            Class clazz = myBean.getClass();
            
            // Determine if the myBean object has MyTestAnnotation annotations
            if (clazz.isAnnotationPresent(MyTestAnnotation.class)) {
                System.out.println("MyBean class configured with MyTestAnnotation annotations!");
                // Get the annotation of the MyTestAnnotation type on the object
                MyTestAnnotation myTestAnnotation = (MyTestAnnotation) clazz.getAnnotation(MyTestAnnotation.class);
                System.out.println(myTestAnnotation.value());
            } else {
                System.out.println("MyBean class not configured with MyTestAnnotation annotations!"); }}catch(Exception e) { e.printStackTrace(); }}}Copy the code

Execute the main method, and the result is as follows

Connected to the target VM, address: '127.0.0.1:62125', transport: 'socket' MyBean class configured with the MyTestAnnotation annotation! Test Disconnected from the target VM, address: '127.0.0.1:62125', transport: 'socket'Copy the code

Annotations and reflections

Deal with annotations

For defined annotations, the annotation can be processed using reflection techniques. Java. Lang. Reflect. AnnotationElement interface provides the functionality. As shown in the figure below, the reflection-related classes Class, Method, and Field all implement the AnnotationElement interface.

So, as soon as we get the Class, Method, Field Class by reflection, we can get the notation we want and evaluate it by getAnnotation(Class). To get annotation information for class methods and fields, common methods include

  • isAnnotationPresent: Determines whether the current element is annotated
  • getAnnotationReturns the specified annotation
  • getAnnotations: Returns all annotations

Demo- Custom annotation verification age

Here is a Demo that shows how to verify age with custom annotations.

  • Define two annotations, one for assignment and one for validation.
/** * @author ZZS */ @documented @Retention(Documented policy.runtime) @Target({ElementType.FIELD,ElementType.METHOD}) @Inherited public @interface InitSex { /** * sex enum * @author zzs */ enum SEX_TYPE {MAN, WOMAN} SEX_TYPE sex() default SEX_TYPE.MAN; }Copy the code
/** * Age check *@author zzs
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
@Inherited
public @interface ValidateAge {
    /** * Minimum */
    int min(a) default 18;
    /**
     * 最大值
     */
    int max(a) default 99;
    /** * Default */
    int value(a) default 20;
}
Copy the code
  • Define the data model. Here withUserClass to represent a specific data object to be processed.
/**
 * user
 *
 * @author zzs
 */
public class User {
    private String username;
    @ValidateAge(min = 20, max = 35, value = 22)
    private int age;
    @InitSex(sex = InitSex.SEX_TYPE.MAN)
    private String sex;
    // omit getter/setter methods
}
Copy the code
  • Test calls. The code is as follows, whereinitUserMethod to demonstrate assigning a value to a property by reflection,checkUserThe reflection method retrieves the value of the current attribute for comparison.

package com.lbs0912.java.demo;

import com.lbs0912.java.demo.annotation.InitSex;
import com.lbs0912.java.demo.annotation.ValidateAge;
import com.lbs0912.java.demo.entity.User;
import java.lang.reflect.Field;

public class TestInitParam {
    public static void main(String[] args) throws IllegalAccessException {
        User user = new User();
        initUser(user);
        Age Unassigned The initialization value is 0, and the verification fails
        boolean checkResult = checkUser(user);
        printResult(checkResult);
        // Reset the age and verify the pass
        user.setAge(22);
        checkResult = checkUser(user);
        printResult(checkResult);
    }
    static void initUser(User user) throws IllegalAccessException {
        // Get all attributes of the User class (getFields cannot get private attributes)
        Field[] fields = User.class.getDeclaredFields();
        // Iterate over all attributes
        for (Field field : fields) {
            System.out.println("field:" + field.getName());
            // If there is an annotation on the property, then the assignment is performed
            if (field.isAnnotationPresent(InitSex.class)) {
                InitSex init = field.getAnnotation(InitSex.class);
                field.setAccessible(true);
                // Set the gender value of the property
                field.set(user, init.sex().toString());
                System.out.println("Complete the modification of the property value to :"+ init.sex().toString()); }}}static boolean checkUser(User user) throws IllegalAccessException {
        // Get all attributes of the User class (getFields cannot get private attributes)
        Field[] fields = User.class.getDeclaredFields();
        boolean result = true;
        // Iterate over all attributes
        for (Field field : fields) {
            // If there is an annotation on the property, then the assignment is performed
            if (field.isAnnotationPresent(ValidateAge.class)) {
                ValidateAge validateAge = field.getAnnotation(ValidateAge.class);
                field.setAccessible(true);
                int age = (int) field.get(user);
                if (age < validateAge.min() || age > validateAge.max()) {
                    result = false;
                    System.out.println("Age value is not eligible."); }}}return result;
    }
    static void printResult(boolean checkResult) {
        if (checkResult) {
            System.out.println("Verified pass");
        } else {
            System.out.println("Verification failed"); }}}Copy the code
  • Print log
The modified value is as follows :MAN Age does not meet the conditions Verification fails Verification succeedsCopy the code

Custom annotation practical application

Custom annotation + interceptor verification login

Here, use custom annotations + Spring Boot interceptor to verify user login. If the @loginRequired annotation is added to the method, the user is prompted that the interface requires a login to access, otherwise no login is required.

  • First, customization@LoginRequiredannotations
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
    
}
Copy the code
  • Create two interface services to accesssourceA.sourceBResources.sourceBAdding interface Access@LoginRequiredAnnotation, indicating login verification.
@RestController
public class IndexController {

    @GetMapping("/sourceA")
    public String sourceA(a){
        return "You are accessing the sourceA resource";
    }

    @GetMapping("/sourceB")
    @LoginRequired
    public String sourceB(a){
        return "You are accessing sourceB resources"; }}Copy the code
  • createSourceAccessInterceptorClass that implements Spring interceptors that use reflection to get annotations and prompt for login
public class SourceAccessInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("We're in the interceptor.");
        System.out.println("\n-------- SourceAccessInterceptor.preHandle --- ");


        // Reflect the LoginRequired annotation on the fetching method
        HandlerMethod handlerMethod = (HandlerMethod)handler;


        Method method = handlerMethod.getMethod();
        if(method.isAnnotationPresent(LoginRequired.class)){
            LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
            // Prompt the user to log in
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().print("You need to log in to access resources");
            System.out.println("methodName:" + method.getName() + ", you need to log in to access resources --");
            
            return false; // Return false for interception
        }


        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("\n-------- SourceAccessInterceptor.postHandle --- ");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("\n-------- SourceAccessInterceptor.afterCompletion --- "); }}Copy the code
  • Realize the Spring classWebMvcConfigurerCreate a configuration class to add interceptors to the interceptor chain
@Configuration
public class InterceptorTrainConfigurer implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SourceAccessInterceptor()).addPathPatterns("/ * *"); }}Copy the code
  • Access pathhttp://localhost:8081/sourceB“, the message “You need to log in to the resources you access” is displayed
Into the interceptor. -- -- -- -- -- -- -- -- SourceAccessInterceptor preHandle -- methodName: sourceB, you access the resources need to log in to -Copy the code

Custom annotation +AOP log printing

  • SpringBoot custom annotations + AOP print logs | Blog

  • Print the entire system execution time | nuggets of each method

  • First, import the dependency packages required by the aspect

<dependency>
      <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Copy the code
  • Create custom annotations@WebLogger
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface WebLogger {

    String value(a) default "";
}
Copy the code
  • Define a facet class and perform log printing logic
package com.lbs.spring.my_app.aspect;

import com.google.gson.Gson;

import com.lbs.spring.my_app.annotation.WebLogger;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
public class WebLoggerAspect {

    @Pointcut("@annotation(com.lbs.spring.my_app.annotation.WebLogger)")
    public void log(a) {}@Around("log()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();

        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable throwable){
            throwable.printStackTrace();
        } finally {
            System.out.println("The Response:"+new Gson().toJson(result));
            System.out.println("Time (ms) :" + (System.currentTimeMillis() - startTime));
        }
        return result;
    }

    @Before("log()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        System.out.println("==================Start=================");
        System.out.println("URL:" + request.getRequestURL().toString());
        System.out.println(Description: "" + getLogValue(joinPoint));
        System.out.println("Method:" + request.getMethod().toString());

        // Prints the controller full path and method
        System.out.println("A Class Method." + joinPoint.getSignature().getDeclaringTypeName() + "," + joinPoint.getSignature().getName());
        System.out.println("Client IP:" + request.getRemoteAddr());

        System.out.println("Request parameters: + new Gson().toJson(joinPoint.getArgs()));

    }

    private String getLogValue(JoinPoint joinPoint) {

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        WebLogger webLogger = method.getAnnotation(WebLogger.class);

        return webLogger.value();
    }

    @After("log()")
    public void doAfter(a) {
        System.out.println("==================End================="); }}Copy the code
  • Finally, create an interface service to show the log printing capability

@RestController
public class IndexController {
    @webLogger (" Student entity ")
    @GetMapping("/sourceC/{source_name}")
    //http://localhost:8081/sourceC/lbs0912
    public String sourceC(@PathVariable("source_name") String sourceName){
        return "You are accessing sourceC resources"; }}Copy the code
  • accesshttp://localhost:8081/sourceC/lbs0912The following logs are displayed
= = = = = = = = = = = = = = = = = = Start = = = = = = = = = = = = = = = = = URL: http://localhost:8081/sourceC/lbs0912 Description: student entity Method: GET a Class Method: com. LBS. Spring. My_app. Controller. IndexController, sourceC client IP: 0:0:0:0:0:0:1-0 request parameters: [" lbs0912 "] = = = = = = = = = = = = = = = = = = End = = = = = = = = = = = = = = = = = the Response: "you are visiting sourceC resources" time-consuming (ms) : 1Copy the code