Java custom annotations are commonly used in the following scenarios: custom annotations + interceptor or AOP, using custom annotations to design your own framework, making your code look elegant. This article will start with the basic concepts of custom annotations, then get into the field, writing small pieces of code to implement custom annotations + interceptors, and custom annotations +AOP.

I. What are annotations?

What are Java annotations, following a quote from Wikipedia

Java annotations, also known as Java annotations, are special syntax metadata added to source code that has been supported since JDK5.0. Classes, methods, variables, parameters, packages, and so on in the Java language can be annotated. Unlike Javadoc, Java annotations can be retrieved through reflection. Annotations can be embedded into the bytecode when the compiler generates the class file. The Java VIRTUAL machine can retain annotation content and obtain annotation content at runtime. It also supports custom Java annotations.

2. Annotation system diagram

Meta-annotations: Meta-annotations are provided in java.lang. annotations, which you can use to define your own annotations. The main use is the Target and Retention annotations

java.lang.reflect.AnnotationElement

3. Common meta-annotations

Target: describes the annotations to modify the scope of object, value in Java. Lang. The annotation. ElementType definition, commonly used include:

  • METHOD: Describes methods
  • PACKAGE: Used to describe packages
  • PARAMETER: Used to describe method variables
  • TYPE: Describes the class, interface, or enum TYPE

Retention: Indicates the Retention time of annotations. Values in Java. Lang. The annotation. RetentionPolicy, values as follows:

  • SOURCE: Valid in the SOURCE file and ignored during compilation
  • CLASS: compiled in a CLASS file with the source file and ignored at run time
  • RUNTIME: this parameter is valid at RUNTIME

We can only get annotations through annotation reflection if we define it as retentionPolicy. RUNTIME. So, suppose we want to define a custom annotation that is used on a field and can be retrieved by reflection to describe the length and function of the field. Can be defined as follows, see my GitHub code.

@Target(ElementType.FIELD)  // Annotations are used on fields
@Retention(RetentionPolicy.RUNTIME)  // Reserved until runtime, available via annotations
public @interface MyField {
    String description(a);
    int length(a);
}
Copy the code
Example – Reflection gets annotations

Define an annotation:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyField {
    String description(a);
    int length(a);
}
Copy the code

Get annotations by reflection

public class MyFieldTest {

    // Use our custom annotations
    @MyField(description = "Username", length = 12)
    private String username;

    @Test
    public void testMyField(a){

        // Get the class template
        Class c = MyFieldTest.class;

        // Get all fields
        for(Field f : c.getDeclaredFields()){
            // Determine if this field has a MyField annotation
            if(f.isAnnotationPresent(MyField.class)){
                MyField annotation = f.getAnnotation(MyField.class);
                System.out.println("Field: [" + f.getName() + "], description: [" + annotation.description() + "], length: [" + annotation.length() +"]"); }}}}Copy the code

The results

Application Scenario 1: Custom annotations and interceptors implement login verification

Next, we use the SpringBoot interceptor to implement a feature that prompts the user that the interface needs to be logged in to access if @loginRequired is added to the method, otherwise no login is required. Start by defining a LoginRequired annotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
    
}
Copy the code

Then write two simple interfaces to access the sourceA, sourceB resources

@RestController
public class IndexController {

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

    @GetMapping("/sourceB")
    public String sourceB(a){
        return "You are accessing sourceB resources"; }}Copy the code

Access successful before adding interceptor

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.");
        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 {}}Copy the code

Implement the Spring class WebMvcConfigurer and create 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

Interception success is as follows

@RestController
public class IndexController {

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

    @LoginRequired
    @GetMapping("/sourceB")
    public String sourceB(a){
        return "You are accessing sourceB resources"; }}Copy the code

Simple logon interception logic

@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("We're in the interceptor.");

        // Reflect the LoginRequred annotation on the fetch method
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class);
        if(loginRequired == null) {return true;
        }

        // There is a LoginRequired annotation, prompting the user to log in
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().print("You need to log in to access resources");
        return false;
    }
Copy the code

SourceB login is required to access sourceB. SourceA login is not required. See GitHub for complete code

Application Scenario 2: Custom annotations and AOP implement log printing

Import the dependency packages required by the section

<dependency>
      <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Copy the code

Define an annotation @mylog

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
    
}
Copy the code

Define a section class as shown in the following code comment:

@Aspect // 1. Indicates that this is a facet class
@Component
public class MyLogAspect {

    // 2. PointCut indicates that this is a PointCut, and @annotation indicates that this PointCut is cut to an annotation followed by the annotation's full class name
    // The most important aspect of a section is the pointcut, around which all the story takes place
    // logPointCut() represents the pointcut name
    @Pointcut("@annotation(me.zebin.demo.annotationdemo.aoplog.MyLog)")
    public void logPointCut(a){};

    // 3. Surround notification
    @Around("logPointCut()")
    public void logAround(ProceedingJoinPoint joinPoint){
        // Get the method name
        String methodName = joinPoint.getSignature().getName();
        // Get the input parameter
        Object[] param = joinPoint.getArgs();

        StringBuilder sb = new StringBuilder();
        for(Object o : param){
            sb.append(o + "; ");
        }
        System.out.println("In [" + methodName + "] method with parameter :" + sb.toString());

        // Continue with the method
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println(methodName + "Method execution completed"); }}Copy the code

In step 2 the IndexController wrote a sourceC to test, with our custom annotations:

    @MyLog
    @GetMapping("/sourceC/{source_name}")
    public String sourceC(@PathVariable("source_name") String sourceName){
        return "You are accessing sourceC resources";
    }
Copy the code

Start the SpringBoot Web project and enter the access address

If you need help or like my article, you are welcome to pay attention to my public account “Donggudonggua”. If you search on wechat, you will be hooked.