Java annotations and custom usage scenarios

Java custom annotation is commonly used scenes: custom annotation + interceptors or AOP, using custom annotations to design their own framework, so that the code looks very elegant. This article will start with the basic concept of the custom annotation talking about, and then began to combat, to write small pieces of code to implement custom annotation + interceptor, custom annotation + AOP.

1. What are annotations (Annotation)

What is Java annotations, the following is quoted from Wikipedia

Java annotations, also known as Java annotations, is JDK5.0 version began to support special syntax for metadata added to the source code.
Java language classes, methods, variables, parameters, and so packets can be marked. And different Javadoc, Java annotation label content can be obtained by reflection. When the compiler generates class files, annotation may be embedded bytecode. Java virtual machine can keep tagging content at runtime can get to the label content. Of course, it also supports custom Java annotation.

II. Annotation System Diagram

Yuan Note: java.lang.annotationprovides meta-annotation, you can use these annotations to define your own notes. Mainly used Target and Retention comment

Yuan notes
Annotation Processing category: Since the annotation defined above, and that we must have a way to get annotation defined ah. java.lang.reflect.AnnotationElement Interface provides this functionality. Annotation process is handled by a java reflection. As follows, reflecting the relevant classes Class, Method, Field have achieved AnnotationElement interface.
Reflection treatment
AnnotationElement interface method
Therefore, as long as we get through reflection Class, Method, Field class, we want to be able to get a comment by getAnnotation (Class) and values.

III. Common yuan notes

The Target : annotation describes a modified target range, the values in java.lang.annotation.ElementTypethe definition, commonly comprising:

  • METHOD: The method described for
  • PACKAGE: package used to describe
  • PARAMETER: variable used to describe the method of
  • TYPE: used to describe a class, interface or enum types

Retention : retention notes indicate the length of time. In value java.lang.annotation.RetentionPolicy, the value is:

  • SOURCE: Effective in the source file, will be ignored during compilation
  • CLASS: With the source file was compiled class files, the runtime ignored
  • RUNTIME: Effective at runtime

Only defined as RetentionPolicy.RUNTIMEthe time that we can get through to annotation notes reflection. So, suppose we want to customize a note, it is used in the field, and can be acquired by reflection, it is used to describe the function and effect of the length field. Can be defined as follows, code, see my GitHub .

@Target(ElementType.FIELD)  //  注解用于字段上
@Retention(RetentionPolicy.RUNTIME)  // 保留到运行时,可通过注解获取
public @interface MyField {
    String description();
    int length();
}
复制代码
Example four - reflective acquired annotation

Define a comment:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyField {
    String description();
    int length();
}
复制代码

Notes acquired by reflection

public class MyFieldTest {

    //使用我们的自定义注解
    @MyField(description = "用户名", length = 12)
    private String username;

    @Test
    public void testMyField(){

        // 获取类模板
        Class c = MyFieldTest.class;

        // 获取所有字段
        for(Field f : c.getDeclaredFields()){
            // 判断这个字段是否有MyField注解
            if(f.isAnnotationPresent(MyField.class)){
                MyField annotation = f.getAnnotation(MyField.class);
                System.out.println("字段:[" + f.getName() + "], 描述:[" + annotation.description() + "], 长度:[" + annotation.length() +"]");
            }
        }

    }
}
复制代码

operation result

operation result

Application Scene One: custom annotation + interceptors implement the login verification

Next, we use springboot interceptors to achieve such a function, if added @LoginRequired on the method, the user is prompted to login to access this interface, you will not need to log in. First define a LoginRequired comment

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
    
}
复制代码

Then write two simple interface, access sourceA, sourceB resources

@RestController
public class IndexController {

    @GetMapping("/sourceA")
    public String sourceA(){
        return "你正在访问sourceA资源";
    }

    @GetMapping("/sourceB")
    public String sourceB(){
        return "你正在访问sourceB资源";
    }

}
复制代码

Success did not visit before adding interceptors

sourceB
HandlerInterceptor achieve spring of interceptors to achieve first class, but not to intercept, simply print the log, as follows:

public class SourceAccessInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("进入拦截器了");
        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 {

    }
}
复制代码

Achieve spring class WebMvcConfigurer, create a configuration class to add to the interceptor interceptor chain

@Configuration
public class InterceptorTrainConfigurer implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SourceAccessInterceptor()).addPathPatterns("/**");
    }
}
复制代码

Successful interception as follows

Intercepted
Add comment @LoginRequired our login method on sourceB

@RestController
public class IndexController {

    @GetMapping("/sourceA")
    public String sourceA(){
        return "你正在访问sourceA资源";
    }

    @LoginRequired
    @GetMapping("/sourceB")
    public String sourceB(){
        return "你正在访问sourceB资源";
    }

}
复制代码

Log in to intercept a simple implementation of logic

@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("进入拦截器了");

        // 反射获取方法上的LoginRequred注解
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        LoginRequired loginRequired = handlerMethod.getMethod().getAnnotation(LoginRequired.class);
        if(loginRequired == null){
            return true;
        }

        // 有LoginRequired注解说明需要登录,提示用户登录
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().print("你访问的资源需要登录");
        return false;
    }
复制代码

Run successfully, you need to log in to access sourceB, access sourceA without logging, complete code, see my GitHub

sourceA
sourceB

Application Scene 2: Custom annotations + AOP realize Log Print

Introducing the first section needs dependencies

<dependency>
      <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
复制代码

Define a comment @MyLog

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
    
}
复制代码

Define a class section, see the following code comments appreciated:

@Aspect // 1.表明这是一个切面类
@Component
public class MyLogAspect {

    // 2. PointCut表示这是一个切点,@annotation表示这个切点切到一个注解上,后面带该注解的全类名
    // 切面最主要的就是切点,所有的故事都围绕切点发生
    // logPointCut()代表切点名称
    @Pointcut("@annotation(me.zebin.demo.annotationdemo.aoplog.MyLog)")
    public void logPointCut(){};

    // 3. 环绕通知
    @Around("logPointCut()")
    public void logAround(ProceedingJoinPoint joinPoint){
        // 获取方法名称
        String methodName = joinPoint.getSignature().getName();
        // 获取入参
        Object[] param = joinPoint.getArgs();

        StringBuilder sb = new StringBuilder();
        for(Object o : param){
            sb.append(o + "; ");
        }
        System.out.println("进入[" + methodName + "]方法,参数为:" + sb.toString());

        // 继续执行方法
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println(methodName + "方法执行结束");

    }
}
复制代码

In step two of IndexController write a sourceC testing, coupled with our custom annotation:

    @MyLog
    @GetMapping("/sourceC/{source_name}")
    public String sourceC(@PathVariable("source_name") String sourceName){
        return "你正在访问sourceC资源";
    }
复制代码

Start springboot web project, enter the access address

Access project
Cut success

Guess you like

Origin juejin.im/post/5d81a92c518825280e3e40dd