Original link:www.ciphermagic.cn/spring-boot…
Updated out-of-the-box version:Github.com/ciphermagic…
This paper introduces an AOP based on Spring Boot and JDK8, combined with custom annotations to achieve universal interface parameter verification.
The reason
Currently, the common method of parameter verification is to add annotations to the entity class, but the verification rules are different for different methods. For example, there is an AccountVO entity:
public class AccountVO {
private String name; / / name
private Integer age; / / age
}
Copy the code
Suppose you have a business where a user needs to fill in their name and age when they register, and only need to fill in their name when they log in. It is not appropriate to apply validation rules to entity classes.
Therefore, I always want to achieve a method level parameter verification. For the same entity parameter, different methods can apply different verification rules, so this tool was born, and has been used in daily work for a long time.
introduce
Here’s how to use it:
@Service
public class TestImpl implements ITestService {
@Override
@Check({"name"."age"})
public void testValid(AccountVO vo) {
// ...}}Copy the code
The @check annotation on the method indicates that the name and age attributes in the AccountVO parameters cannot be null. In addition to non-null check, it also supports size judgment and equal check:
@Check({"id>=8"."name! =aaa"."title<10"})
Copy the code
The default error message returns the field, the reason for the error, and the method called, for example:
updateUserId must not null while calling testValid
id must >= 8 whilecalling testValid name must ! = aaawhile calling testValid
Copy the code
Custom error return messages are also supported:
@Check({"Title <=8: The title should be no more than 8 words, including punctuation"})
public void testValid(TestPO po) {
// ...
}
Copy the code
To replace the default error information, add: to the verification rule and add custom information.
PS: The core principle is to obtain the value of the field in the parameter entity through reflection, and then verify it according to the rules. Therefore, only methods containing one parameter are supported at present, and the parameter cannot be the basic type.
use
How to use AOP in Spring-Boot is not described here, mainly introduces the core code in AOP.
Maven rely on
In addition to the spring-boot dependencies, the required third-party dependencies, not core dependencies, can be selected according to personal habits:
<! String validation -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2 rainfall distribution on 10-12</version>
</dependency>
<! -- For log printing -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
Copy the code
Custom annotations
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
* Created by cipher on 2017/9/20. */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RUNTIME)
public @interface Check {
// Field verification rule format: field name + verification rule + colon + error message, for example :ID <10: The ID must be less than 10
String[] value();
}
Copy the code
The core code
The interface method with the @check annotation is intercepted by the section. Before the method is executed, the parameter verification is performed. If there is an error message, the system returns:
@Around(value = "@com.cipher.checker.Check") // The path of the custom annotation is changed here
public Object check(ProceedingJoinPoint point) throws Throwable {
Object obj;
// Check parameters
String msg = doCheck(point);
if(! StringUtils.isEmpty(msg)) {// It is possible to return its own wrapped return class
throw new IllegalArgumentException(msg);
}
obj = point.proceed();
return obj;
}
Copy the code
In the doCheck method, the main principle is to obtain the field name and verification rule specified on the annotation, obtain the value of the corresponding field in the parameter entity through reflection, and then verify:
/** ** *@param point ProceedingJoinPoint
* @returnError message */
private String doCheck(ProceedingJoinPoint point) {
// Get method parameter values
Object[] arguments = point.getArgs();
// Get method
Method method = getMethod(point);
String methodInfo = StringUtils.isEmpty(method.getName()) ? "" : " while calling " + method.getName();
String msg = "";
if (isCheck(method, arguments)) {
Check annotation = method.getAnnotation(Check.class);
String[] fields = annotation.value();
Object vo = arguments[0];
if (vo == null) {
msg = "param can not be null";
} else {
for (String field : fields) {
// Parse the fields
FieldInfo info = resolveField(field, methodInfo);
// Get the value of the field
Object value = ReflectionUtil.invokeGetter(vo, info.field);
// Execute the verification ruleBoolean isValid = info.optEnum.fun.apply(value, info.operatorNum); msg = isValid ? msg : info.innerMsg; }}}return msg;
}
Copy the code
You can see that the main logic is:
Parse the field -> Get the value of the field -> Execute the validation rule
Internally maintains an enumeration class in which the relevant validation operations are specified:
/** * enumerates */
enum Operator {
/** * is greater than */
GREATER_THAN(">", CheckParamAspect::isGreaterThan),
/** * is greater than or equal to */
GREATER_THAN_EQUAL("> =", CheckParamAspect::isGreaterThanEqual),
/**
* 小于
*/
LESS_THAN("<", CheckParamAspect::isLessThan),
/** * is less than or equal to */
LESS_THAN_EQUAL("< =", CheckParamAspect::isLessThanEqual),
/** ** does not equal */
NOT_EQUAL(! "" =", CheckParamAspect::isNotEqual),
/** * is not null */
NOT_NULL("not null", CheckParamAspect::isNotNull);
private String value;
private BiFunction<Object, String, Boolean> fun;
Operator(String value, BiFunction<Object, String, Boolean> fun) {
this.value = value;
this.fun = fun; }}Copy the code
For reasons of space, I don’t want to go through all of the code here. If you are interested, you can go to cipherMagic /java-learn/sandbox/ Checker for all of the source code
TODO
- Packaged as a stand-alone component as a Spring Boot Starter
- Regular expression verification is supported
The last
Thank you for reading. If you like, please give a thumbs up on Github. If you have any questions or suggestions, please leave a comment below.