Spring custom annotation gameplay, from entry to...

Author: how sweet it in the link: https://juejin.im/post/5cf376e16fb9a07eee5eb6eb

In the business development process, we will encounter all kinds of annotations, but the framework's own annotations are not always able to meet complex business needs. We can customize annotations to meet our needs.

According to the location of annotations, the article will be divided into field annotations, methods, and class annotations to introduce custom annotations.

Field annotation

Field annotations are generally used to verify whether the field meets the requirements. hibernate-validateDependency provides a lot of verification annotations, such as @NotNull, @Rangeetc., but these annotations are not able to meet all business scenarios.

For example, if we want the incoming parameters to be in the specified Stringset, then the existing annotations can't meet the needs and need to be implemented by ourselves.

Custom annotation

Define an @Checkannotation, by @interfacedeclaring an annotation

@Target({ ElementType.FIELD}) //只允许用在类的字段上
@Retention(RetentionPolicy.RUNTIME) //注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解
@Constraint(validatedBy = ParamConstraintValidated.class)
public @interface Check {
    /**
     * 合法的参数值
     * */
    String[] paramValues();

    /**
     * 提示信息
     * */
    String message() default "参数不为指定值";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

@Target defines where the annotation is used to indicate that the annotation can be declared before those elements.

ElementType.TYPE: Indicates that the annotation can only be declared in front of a class.

ElementType.FIELD: Indicates that the annotation can only be declared in front of a field of a class.

ElementType.METHOD: Indicates that this annotation can only be declared before a method of a class.

ElementType.PARAMETER: It means that the annotation can only be declared before a method parameter.

ElementType.CONSTRUCTOR: It means that the annotation can only be declared before the construction method of a class.

ElementType.LOCAL_VARIABLE: It means that the annotation can only be declared before a local variable.

ElementType.ANNOTATION_TYPE: It means that the annotation can only be declared before one annotation type.

ElementType.PACKAGE: It means that the annotation can only be declared before a package name

@Constraint specifies the validator associated with the annotation by using validatedBy

@Retention is used to describe the life cycle of the annotation class.

RetentionPolicy.SOURCE: Annotations are only kept in the source file

RetentionPolicy.CLASS : Annotations are kept in the class file and discarded when loaded into the JVM virtual machine

RetentionPolicy.RUNTIME: Annotations are kept during the running of the program. At this time, all annotations defined on a certain class can be obtained through reflection.

Validator class

The validator class needs to implement a ConstraintValidatorgeneric interface

public class ParamConstraintValidated implements ConstraintValidator<Check, Object> {
    /**
     * 合法的参数值,从注解中获取
     * */
    private List<String> paramValues;

    @Override
    public void initialize(Check constraintAnnotation) {
        //初始化时获取注解上的值
        paramValues = Arrays.asList(constraintAnnotation.paramValues());
    }

    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
        if (paramValues.contains(o)) {
            return true;
        }
}

The first generic parameter type Check: annotation, the second generic parameter Object: check field type. Need to realize initializeand isValidmethod, isValidmethod is verification logic, initializemethod initialization work

How to use

Define an entity class

@Data
public class User {
    /**
     * 姓名
     * */
    private String name;

    /**
     * 性别 man or women
     * */
    @Check(paramValues = {"man", "woman"})
    private String sex;
}

sexCheck the field, its value must be womanorman

test

@RestController("/api/test")
public class TestController {
    @PostMapping
    public Object test(@Validated @RequestBody User user) {
        return "hello world";
    }
}

Note that you need Userto add @Validatedannotations to the object. You can also use @Validannotations here . The difference between @Validated and @Valid, see this suggestion.

Method, class annotation

Encountered in the process of developing such a demand, as only authorized users can access methods in this class or a specific method, when the first lookup data from the database does not look, start with guava cachethe look, from the redislook , And finally find mysql(multi-level cache).

At this time, we can customize annotations to complete this requirement. The first scenario is to define a permission verification annotation, and the second scenario is to define a spring-data-redissimilar @Cacheableannotation under the package .

Permission annotation

Custom annotation

@Target({ ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionCheck {
    /**
     * 资源key
     * */
    String resourceKey();
}

The scope of the annotation is on the class or method

Interceptor class

public class PermissionCheckInterceptor extends HandlerInterceptorAdapter {
    /**
     * 处理器处理之前调用
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        HandlerMethod handlerMethod = (HandlerMethod)handler;
        PermissionCheck permission = findPermissionCheck(handlerMethod);

        //如果没有添加权限注解则直接跳过允许访问
        if (permission == null) {
            return true;
        }

        //获取注解中的值
        String resourceKey = permission.resourceKey();

        //TODO 权限校验一般需要获取用户信息,通过查询数据库进行权限校验
        //TODO 这里只进行简单演示,如果resourceKey为testKey则校验通过,否则不通过
        if ("testKey".equals(resourceKey)) {
            return true;
        }

        return false;
    }

    /**
     * 根据handlerMethod返回注解信息
     *
     * @param handlerMethod 方法对象
     * @return PermissionCheck注解
     */
    private PermissionCheck findPermissionCheck(HandlerMethod handlerMethod) {
        //在方法上寻找注解
        PermissionCheck permission = handlerMethod.getMethodAnnotation(PermissionCheck.class);
        if (permission == null) {
            //在类上寻找注解
            permission = handlerMethod.getBeanType().getAnnotation(PermissionCheck.class);
        }

        return permission;
    }
}

The logic of permission verification is that you can access if you have permission, and access is not allowed if you don’t. The essence is actually an interceptor. We first need to get the annotation, and then get the field on the annotation for verification, the verification is passed and the return is passed true, otherwise it is returnedfalse

test

 @GetMapping("/api/test")
 @PermissionCheck(resourceKey = "test")
 public Object testPermissionCheck() {
     return "hello world";
 }

This method requires permission verification, so an PermissionCheckannotation is added .

Cache annotation

Custom annotation

@Target({ ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomCache {
    /**
     * 缓存的key值
     * */
    String key();
}

Annotations can be used on methods or classes, but caching annotations are generally used on methods. Pay attention to the public number Java technology stack and reply to spring in the background to get the most complete Spring tutorials I have compiled.

section

@Aspect
@Component
public class CustomCacheAspect {
    /**
     * 在方法执行之前对注解进行处理
     *
     * @param pjd
     * @param customCache 注解
     * @return 返回中的值
     * */
    @Around("@annotation(com.cqupt.annotation.CustomCache) && @annotation(customCache)")
    public Object dealProcess(ProceedingJoinPoint pjd, CustomCache customCache) {
        Object result = null;

        if (customCache.key() == null) {
            //TODO throw error
        }

        //TODO 业务场景会比这个复杂的多,会涉及参数的解析如key可能是#{id}这些,数据查询
        //TODO 这里做简单演示,如果key为testKey则返回hello world
        if ("testKey".equals(customCache.key())) {
            return "hello word";
        }

        //执行目标方法
        try {
            result = pjd.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return result;
    }
}

Because the cached annotation needs to have a return value before the method is executed, this annotation is not processed by the interceptor, but the annotation is processed before the method is executed by using the aspect.

If the annotation has no return value, it will return the value in the method

test

@GetMapping("/api/cache")
@CustomCache(key = "test")
public Object testCustomCache() {
    return "don't hit cache";
}

Recommend to read more on my blog:

1. A series of tutorials on Java JVM, collections, multithreading, and new features

2. Spring MVC, Spring Boot, Spring Cloud series of tutorials

3. Maven, Git, Eclipse, Intellij IDEA series tool tutorial

4. The latest interview questions from major manufacturers such as Java, back-end, architecture, and Alibaba

Feel good, don’t forget to like + forward!

Finally, pay attention to the WeChat official account of the stack leader: Java technology stack, reply: Welfare, you can get a free copy of the latest Java interview questions I have compiled for 2020. It is really complete (including answers), without any routine.

Guess you like

Origin blog.csdn.net/youanyyou/article/details/108397147