Spring custom annotations (normal and attribute verification) manual verification, verification and log printing

1. Prepare the environment

1.1 Introduce pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.yhy</groupId>
    <artifactId>custom-annotation</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>custom-annotation</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.13</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2. Use aop custom annotations

annotation

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnalysisActuator {
    
    
    String note() default "";
}

AOP
notification needs to use a custom annotation class to facilitate getting the annotation information. It is written in the serviceStatistics method parameter and spring automatically injects it.

@Aspect
@Component
public class AnalysisActuatorAspect {
    
    
    final static Logger log = LoggerFactory.getLogger(AnalysisActuatorAspect.class);

    ThreadLocal<Long> beginTime = new ThreadLocal<>();

	
    @Pointcut("@annotation(analysisActuator)")
    public void serviceStatistics(AnalysisActuator analysisActuator) {
    
    
    }

    @Before("serviceStatistics(analysisActuator)")
    public void doBefore(JoinPoint joinPoint, AnalysisActuator analysisActuator) {
    
    
        // 记录请求到达时间
        beginTime.set(System.currentTimeMillis());
        log.info("note:{}", analysisActuator.note());
    }

    @After("serviceStatistics(analysisActuator)")
    public void doAfter(AnalysisActuator analysisActuator) {
    
    
        log.info("statistic time:{}, note:{}", System.currentTimeMillis() - beginTime.get(), analysisActuator.note());
    }

}

service

@Service
public class HelloWorldService {
    
    

    @AnalysisActuator(note = "获取聊天信息方法")
    public String getHelloMessage(String name) {
    
    
        return "Hello " + Optional.ofNullable(name).orElse("World!");
    }

}

controller

@RestController
@RestController
public class HelloWorldController {
    
    
    @Autowired
    private HelloWorldService helloWorldService;


    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello(String name) {
    
    
        return helloWorldService.getHelloMessage(name);
    }
}

Visit http://127.0.0.1:8081/hello
results

note:获取聊天信息方法
statistic time:0, note:获取聊天信息方法

3. Customized verification annotations

Requirement : Import each row of table data and encapsulate it into a class, and then verify the legality of the data. If there is an error in the data, an error message will be returned. Problem: If there are many
attributes and each if is judged, then you have to write a lot of if, which is not an elegant
solution . : Complete the custom verification annotation by implementing ConstraintValidator, fill in the error information, and then verify manually. If there is an error, the error information will be obtained.

@Constraint specifies the verification implementation class. Two attributes, groups and payload, must be added (required to implement ConstraintValidator)

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {
    
    ExcelDataValidator.class})
public @interface ExcelDataVaild {
    
    

    int min() default 2;

    int max() default 255;

    String message() default "";

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

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

The verification class needs to implement the ConstraintValidator interface.
The interface uses generics and needs to specify two parameters, the first is a custom annotation class, and the second is the data type that needs to be verified.
After implementing the interface, you need to override two methods, namely the initialize method and the isValid method. Among them, initialize is the initialization method, and you can do some initialization operations in it. The isValid method is the verification method we ultimately need, and specific verification steps can be implemented in this method.

public class ExcelDataValidator implements ConstraintValidator<ExcelDataVaild, String> {
    
    

    private int min;
    private int max;

    @Override
    public void initialize(ExcelDataVaild excelDataVaild) {
    
    
        min = excelDataVaild.min();
        max = excelDataVaild.max();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
    
    
        if (StrUtil.isBlank(value) || value.length() < min || value.length() > max) {
    
    
            return false;
        }
        return true;
    }
}

Classes that need to be verified

@Data
public class User {
    
    

    @ExcelDataVaild(min = 2, max = 6,message = "名字错误")
    private String name;
    @ExcelDataVaild(min = 11, max = 11,message = "手机号错误")
    private String phone;
    @ExcelDataVaild(min = 5, max = 32,message = "地址错误")
    private String address;
}

Manual verification tools

@Slf4j
public class ValidatorUtils {
    
    

    private static Validator validatorFast = Validation.byProvider(HibernateValidator.class).configure().failFast(true).buildValidatorFactory().getValidator();
    private static Validator validatorAll = Validation.byProvider(HibernateValidator.class).configure().failFast(false).buildValidatorFactory().getValidator();

    /**
     * 校验遇到第一个不合法的字段直接返回不合法字段,后续字段不再校验
     *
     * @param <T>
     * @param domain
     * @return
     * @throws Exception
     * @Time 2020年6月22日 上午11:36:13
     */
    public static <T> Set<ConstraintViolation<T>> validateFast(T domain) {
    
    
        Set<ConstraintViolation<T>> validateResult = validatorFast.validate(domain);
        if (validateResult.size() > 0) {
    
    
            log.info(validateResult.iterator().next().getPropertyPath() + ":" + validateResult.iterator().next().getMessage());
        }
        return validateResult;
    }

    /**
     * 校验所有字段并返回不合法字段
     *
     * @param <T>
     * @param domain
     * @return
     * @throws Exception
     * @Time 2020年6月22日 上午11:36:55
     */
    public static <T> Set<ConstraintViolation<T>> validateAll(T domain) {
    
    
        Set<ConstraintViolation<T>> validateResult = validatorAll.validate(domain);
        if (validateResult.size() > 0) {
    
    
            Iterator<ConstraintViolation<T>> it = validateResult.iterator();
            while (it.hasNext()) {
    
    
                ConstraintViolation<T> cv = it.next();
                log.info(cv.getPropertyPath() + ":" + cv.getMessage());
            }
        }
        return validateResult;
    }

}

Business Layer

@Service
public class UserService {
    
    
    public void vaildUser() {
    
    
    	//假装读取excel表格数据并封装到user
        User user = new User();
        user.setName("1");
        user.setPhone("1231233213");
        user.setAddress("eeddd");
        Set<ConstraintViolation<User>> validateResult = null;
        try {
    
    
            validateResult = ValidatorUtils.validateAll(user);

            if (validateResult.size() > 0) {
    
    
                //获取检验信息,进行业务处理
                System.out.println(validateResult.iterator().next().getMessage());
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

control layer

@RestController
public class UserController {
    
    
    @Autowired
    private UserService userService;

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public void user() {
    
    
        userService.vaildUser();
    }
}

Visit http://127.0.0.1:8081/user
results

phone:手机号错误
name:名字错误

Perform request parameter verification and output on the request parameter object with the annotation javax.validation.Valid

Customize an annotation

/**
 * 对带有注解 javax.validation.Valid 的请求参数对象 进行 请求参数检验和输出
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ReqParamValidAndLog {
    
    
}

The aspect
first outputs the request parameters, and then verifies it. If it fails, the verification information will be returned.

@Aspect
@Component
@Slf4j
public class ReqParamValidAndLogAspect {
    
    

    @Around("@annotation(com.aspect.ReqParamValidAndLog)")
    public Object validateParameters(ProceedingJoinPoint joinPoint) throws Throwable {
    
    
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        String[] parameterNames = methodSignature.getParameterNames();

        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
    
    
            Annotation[] parameterAnnotations = method.getParameterAnnotations()[i];
            for (Annotation annotation : parameterAnnotations) {
    
    
                if (annotation.annotationType().equals(Valid.class)) {
    
    
                    Object req = args[i];

                    String className = method.getDeclaringClass().getName();
                    String methodName = method.getName();
                    String fullMethodName = className + "." + methodName;

                    String reqParamName = parameterNames[0];

                    log.info("请求参数打印,方法 {},请求参数对象名{}:{}", fullMethodName, reqParamName, JSON.toJSONString(req));
                    try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
    
    
                        Validator validator = factory.getValidator();
                        Set<ConstraintViolation<Object>> violations = validator.validate(req);
                        //返回检验不通过信息
                        if (CollUtil.isNotEmpty(violations)) {
    
    
                            ConstraintViolation<Object> violation = violations.iterator().next();
                            return Result.ofFailMsg(violation.getPropertyPath() + " " + violation.getMessage());
                        }
                    }
                }
            }
        }
        return joinPoint.proceed(args);
    }
}

Request class

@Data
public class TraceLabelParentChildBindOfBatchReq  {
    
    

    @NotEmpty
    private String code;
    @NotNull
    private List<String> childCodeList;

}

The control layer
annotation is written on the method, and the parameters with the @Valid annotation will be printed and verified, and an error message will be returned if an error occurs.

    @PostMapping("parentChildBindOfBatch")
    @ReqParamValidAndLog
    public Result<String> parentChildBindOfBatch(@Valid @RequestBody TraceLabelParentChildBindOfBatchReq req) {
    
    
        return Result.ofSuccessMsg("绑定成功");
    }

    @PostMapping("parentChildBindOfCsvFile")
    @ReqParamValidAndLog
    public Result<String> parentChildBindOfCsvFile(@RequestPart("csvFile") MultipartFile csvFile, @Valid TraceLabelParentChildBindOfCsvFileReq req) {
    
    
        return Result.ofSuccessMsg("绑定成功");
    }

Guess you like

Origin blog.csdn.net/Fire_Sky_Ho/article/details/120693085