(Comprehension of full annotations) Explanation of verification business based on AOP + custom annotations

Directory

Quote:

final effect:

Implementation steps

Customize two annotations

Define a regular enumeration class

Define the aspect class (important!)

Validation class:

Quotation:

This technical solution can be used for many verification types of businesses (login status, permissions…), here we start with the parameter verification business (the specific implementation is relatively simple, it is important to understand the realization of the technical solution) , I hope that everyone can appreciate the benefits of AOP + custom annotations, and further understand the usage patterns of AOP and custom annotations, not only limited to theoretical concepts, but also to implement knowledge into practice. Every step of the code is well-composed with comments, I believe everyone can understand every step.

Final effect:

It is said that this kind of plan is good, but what does it look like? Let’s take a look at the final implementation scenario

Through two annotations, you can customize the verification rules for different parameters, which is convenient and quick, once and for all, and the coupling degree of this scheme is very low, which conforms to the principle of opening and closing. The new verification scheme does not need to change the original Code

Steps to Implementation

Customize two annotations

/**
 * @author global parameter validator
 */
@Retention(RetentionPolicy. RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface VerifyParam {

    int min() default -1;
    int max() default -1;
    boolean required() default false;

    // Define regular check, no check by default
    VerifyRegexEnum regex() default VerifyRegexEnum.NO;
}
/**
 * @author wzx
 */
@Target({ElementType. METHOD})
@Retention(RetentionPolicy. RUNTIME)
@Documented
@Mapping
public @interface GlobalInterceptor {
    /**
     * Check parameter
     * @return
     */
    boolean checkParams() default false;
}

@GlobalInterceptor: It acts as a tangent locator, and uses this annotation as the entry point of the verification logic

@VerifyParam: Determine the custom verification type and pass a certain range of data for the customization of the verification process

Define regular enumeration class

Customize a regular enumeration to meet the parameter selection of @VerifyParem. In this class, you can set a series of verification type rules. Selecting different verification rules can play a corresponding verification process

/**
 * @author wzx
 * Define regular enumeration
 */

public enum VerifyRegexEnum {

    NO("", "No validation"),
    EMAIL("^[a-zA-Z0-9] + ([-_.][a-zA-Z0-9] + )*@[qQ][qQ]\.com$", "Mailbox"),
    PASSWORD("^[a-zA-Z0-9!@#$%^ & amp;*()_ + \-=\[\]{};':\\ "\\|,.<>\/?]{8,18}$", "It can only be numbers, letters, special characters 8-18");

    private String regex;
    private String desc;
    VerifyRegexEnum(String regex, String desc) {
        this.regex = regex;
        this.desc = desc;
    }

    public String getRegex(){return regex;}
    public String getDesc(){return desc; }
}

Define the aspect class (emphasis!)

In this class, it is mainly used to perform specific verification logic operations, and the overall process can be divided into

  • First lock the point of cut by scanning custom annotations
  • Cutting into the cutting plane.
  • Lock to the method through the annotation, and get the parameter list, annotation, method name and other data of the method through reflection
  • According to the obtained @GlobalInterceptor object, obtain its internal checkParams data value to determine whether to perform verification
  • If the verification is performed, the parameter list and other data of the method will be sent to the verification module for verification
  • Loop through the actual parameter list and find the parameter containing @VerifyParam
  • Branch judgment is performed on this parameter, and two verification schemes are divided according to whether the parameter is a basic data type or a reference data type
  • For the basic data type checks around 1. Whether it is empty 2. Whether the length is appropriate 3. Whether it meets the regular expression of your choice
  • For reference data type verification, the above verification logic is mainly performed through violent reflection
/**
 * @author wzx
 * Global parameter validation aspect
 */
@Aspect
@Component
public class GlobalOperationAspect {

    // It is used to judge whether the parameter to be verified is of these basic types, if not, the parameter is regarded as an object for verification
    private static final String[] TYPE_BASE = {"java. lang. String", "java. lang. Integer", "java. lang. Long"};

    // Define a pointcut. Here, the custom annotation is used as a cut point. In fact, the purpose of this step is to let us lock the position to be woven into the cut surface
    @Pointcut("@annotation(com.example.bfwp.annotation.GloballInterceptor)")
    private void requestInterceptor(){

    }


    @Before("requestInterceptor()")
    public void interceptorDo(JoinPoint point){
        try {
        // Obtain all the objects marked by the annotation through reflection
            // Get the target object where the pointcut is located
            Object target = point. getTarget() ;
            // Get the parameter list of the method where the pointcut is located
            Object[] arguments = point. getArgs();
            // Get the method name of the method where the pointcut is located
            String methodName = point. getSignature(). getName();
            // Get the type of the parameter list of the method where the pointcut is located
            Class<?>[] parameterTypes = ((MethodSignature) point. getSignature()). getMethod(). getParameterTypes();
            // Obtain the method object through method name + type of parameter list + reflection
            Method method = target.getClass().getMethod(methodName, parameterTypes);
            // Get the global interceptor (that is, the custom annotation) - this step is mainly to obtain the attribute data of the custom annotation
            GlobalInterceptor interceptor = method. getAnnotation(Global Interceptor. class);
            // Obtain the attribute data of custom annotations for logical operations
            // Whether to verify the parameters
            if (interceptor. checkParams()){
                // Throw in the parameters for validation
                validateParams(method, arguments);
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Check parameter
     * @param method check method
     * @param arguments check method object
     */
    private void validateParams(Method method, Object[] arguments) {
        // Get the formal parameter group of the validation method
        Parameter[] parameters = method. getParameters();
        for (int i = 0; i < parameters. length; i ++ ) {
            // Get a specific formal parameter object
            Parameter parameter = parameters[i];
            // Get a specific actual parameter object
            Object value = arguments[i];
            // Find the parameters containing @VerfyParam, that is to say, find the parameters to be verified
            VerifyParam verifyParam = parameter. getAnnotation(VerifyParam. class);
            if (verifyParam == null){
                // If it is empty, it means that the parameter has not been added with the custom annotation, and no verification is required
                continue;
            }
            // If it is not empty, it means that the custom annotation has been added to the parameter and needs to be verified
            /* There are two cases for parameter verification
                1. Basic data types (and their wrappers)
                2. Reference data type
              * */
            if (ArrayUtils.contains(TYPE_BASE,parameter.getParameterizedType().getTypeName())){
                // Included means that the parameter is a basic data type
                checkValue(value, verifyParam);
            } else {
                // If it does not contain, it means that the parameter is a reference data type
                checkObjectValue(parameter, value);
            }
        }
    }

    /**
     * Reference data type validation
     * @param parameter The formal parameter object of the verification object
     * @param object the object to check
     */
    private void checkObjectValue(Parameter parameter, Object object){
        try {
            // Get parameter type
            String typeName = parameter.getParameterizedType() .getTypeName();
            // Get the bytecode file of the object's class
            Class<?> classz = Class. forName(typeName);
            // Get all attribute objects in this class
            Field[] fields = classz. getDeclaredFields();
            for (Field field : fields){
                // See if the property has a validation annotation set
                VerifyParam fieldVerifyParam = field. getAnnotation(VerifyParam. class);
                if (fieldVerifyParam == null){
                    // Attributes that do not need to be checked are skipped
                    continue;
                }
                // Attributes that need to be verified, first skip violent reflection to ensure that you can get their values, because general attributes are modified as private
                field.setAccessible(true);
                // get attribute value
                Object resultValue = field. get(object);
                checkValue(resultValue, fieldVerifyParam);
            }
        } catch (ClassNotFoundException e) {
            //todo
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            //todo
            throw new RuntimeException(e);
        }
    }

    /**
     * Validation logic of basic data types (and their packaging classes)
     * @param value specific data value (actual parameter object)
     * @param verifyParam Annotation object annotating this parameter (used to obtain its attribute data)
     */
    private void checkValue(Object value, VerifyParam verifyParam) {
        // First, see if he really wants to check if he added the annotation
        if (!verifyParam.required()) {return;}
        Boolean isEmpty = value == null || "".equals(value.toString());
        Integer length = value == null ? 0 : value .toString () .length();
         /**
         * check if empty
         */
        if (isEmpty) {
            // TODO is empty
            throw new RuntimeException("The passed value cannot be empty");
        }
        /**
         * check length
         */
        if ((verifyParam.max() != -1 & amp; & amp; verifyParam.max() < length || verifyParam.min() != -1 & amp; & amp; verifyParam.min() > length) ) {
            // TODO length is wrong
            throw new RuntimeException("The length of the passed value is incorrect");
        }
        /**
         * Validation regularity
         */
        if (!VerifyRegexEnum.NO.equals(verifyParam.regex()) & amp; & amp; !VerifyUtils.verify(verifyParam.regex(), String.valueOf(value))){
            // TODO regex check is incorrect
            throw new RuntimeException("regular check is incorrect");
        }
    }
}

Validation class:

/**
 * @author wzx
 */
public class VerifyUtils {

    /**
     * The eligibility of regular check parameters
     * @param regs regular expression
     * @param value the value to validate
     * @return check result
     */
    public static Boolean verify(String regs, String value){
        Pattern pattern = Pattern.compile(regs);
        Matcher matcher = pattern. matcher(value);
        return matcher. matches();
    }

    public static Boolean verify(VerifyRegexEnum verifyRegexEnum, String value){
        return verify(verifyRegexEnum.getRegex(), value);
    }
}