1. 環境を準備する
1.1 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. aop カスタム アノテーションを使用する
注釈
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnalysisActuator {
String note() default "";
}
AOP
通知では、アノテーション情報の取得を容易にするために、serviceStatistics メソッドのパラメーターに記述され、Spring によって自動的に挿入されるカスタム アノテーション クラスを使用する必要があります。
@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
public class HelloWorldService {
@AnalysisActuator(note = "获取聊天信息方法")
public String getHelloMessage(String name) {
return "Hello " + Optional.ofNullable(name).orElse("World!");
}
}
コントローラ
@RestController
@RestController
public class HelloWorldController {
@Autowired
private HelloWorldService helloWorldService;
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String hello(String name) {
return helloWorldService.getHelloMessage(name);
}
}
http://127.0.0.1:8081/hello
の結果にアクセスしてください
note:获取聊天信息方法
statistic time:0, note:获取聊天信息方法
3. カスタマイズされた検証注釈
要件: テーブル データの各行をインポートし、それをクラスにカプセル化し、データの正当性を検証します。データにエラーがある場合は、エラー メッセージが返されます。 問題: 多数の
属性があり、それぞれが if である場合と判断されると、たくさんの if を書かなければなりませんが、これはエレガントな
解決策ではありません : ConstraintValidator を実装してカスタム検証アノテーションを完成させ、エラー情報を入力し、手動で検証します。エラーがある場合は、エラー情報が表示されます。が得られます。
@Constraint は検証実装クラスを指定します。グループとペイロードの 2 つの属性を追加する必要があります (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 {
};
}
検証クラスは ConstraintValidator インターフェイスを実装する必要があります。
このインターフェイスはジェネリックスを使用し、2 つのパラメーターを指定する必要があります。1 つ目はカスタム アノテーション クラスで、2 つ目は検証する必要があるデータ型です。
インターフェイスを実装した後、initialize メソッドと isValid メソッドという 2 つのメソッドをオーバーライドする必要があります。このうちinitializeは初期化メソッドであり、その中でいくつかの初期化操作を行うことができます。isValid メソッドは最終的に必要な検証メソッドであり、このメソッドには特定の検証手順を実装できます。
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;
}
}
検証が必要なクラス
@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;
}
手動検証ツール
@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;
}
}
ビジネス層
@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();
}
}
}
制御層
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/user", method = RequestMethod.GET)
public void user() {
userService.vaildUser();
}
}
http://127.0.0.1:8081/user
resultsにアクセスしてください
phone:手机号错误
name:名字错误
リクエストパラメータの検証を実行し、アノテーション javax.validation.Valid を付けてリクエストパラメータオブジェクトに出力します。
注釈をカスタマイズする
/**
* 对带有注解 javax.validation.Valid 的请求参数对象 进行 请求参数检验和输出
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ReqParamValidAndLog {
}
アスペクトは
リクエストパラメータを出力して検証し、失敗した場合は検証情報を返します。
@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);
}
}
リクエストクラス
@Data
public class TraceLabelParentChildBindOfBatchReq {
@NotEmpty
private String code;
@NotNull
private List<String> childCodeList;
}
制御層の
アノテーションがメソッドに書き込まれ、@Valid アノテーションが付いたパラメータが出力および検証され、エラーが発生した場合はエラー メッセージが返されます。
@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("绑定成功");
}