This article introduces writing an AOP based on Spring Boot and JDK8, combining custom annotations to achieve common interface parameter verification.
reason
Currently, the commonly used method of parameter verification is to add annotations on the entity class, but for different methods, the verification rules applied are also different. For example, there is an AccountVO entity:
public class AccountVO { private String name; // Name private Integer age; // Age}Suppose there is a business: when a user registers, he or she needs to fill in his or her name, and when a user logs in, he or she only needs to fill in his or her name. Then it is obviously inappropriate to add verification rules to entity classes.
Therefore, I have always wanted to implement a method-level parameter verification. Different methods can apply different verification rules for the same entity parameters, which gave birth to this tool and has been used in daily work for a long time.
introduce
Let’s take a look at how to use it first:
@Servicepublic class TestImpl implements ITestService { @Override @Check({"name", "age"}) public void testValid(AccountVO vo) { // ... }}The @Check annotation on the method indicates that the name and age properties in the parameter AccountVO cannot be empty. In addition to non-empty verification, it also supports size judgment and equality verification:
@Check({"id>=8", "name!=aaa", "title<10"})The default error message returns fields, error causes and called methods, such as:
updateUserId must not null while calling testValidid must >= 8 while calling testValidname must != aaa while calling testValid
Custom error return information is also supported:
@Check({"title<=8: The number of title characters does not exceed 8 characters, including punctuation marks"})public void testValid(TestPO po) { // ...}Just add: after the verification rule and write custom information, which will replace the default error message.
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, currently only methods containing one parameter are supported, and the parameters cannot be of the basic type.
use
How to use AOP in spring-boot I will not go into details here, and mainly introduce the core code in AOP.
Maven dependencies
In addition to spring-boot dependencies, the required third-party dependencies are not core dependencies, and you can choose according to your personal habits:
<!-- for string verification--><dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.3.2</version></dependency><!-- for log printing--><dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version></dependency>
Custom annotations
import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.Target;import static java.lang.annotation.RetentionPolicy.RUNTIME;/** * Parameter verification annotation* Created by cipher on 2017/9/20. */@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RUNTIME)public @interface Check { // Field verification rules, format: field name + verification rules + colon + error message, for example: id < 10: ID must be less than 10 String[] value();}Core code
By intercepting the interface method with @Check annotation, perform parameter verification before the method is executed. If there is an error message, it will be returned directly:
@Around(value = "@com.cipher.checker.Check") // Here you need to change to the path of custom annotation public Object check(ProceedingJoinPoint point) throws Throwable { Object obj; // Parameter verification String msg = doCheck(point); if (!StringUtils.isEmpty(msg)) { // Here you can return the return class encapsulated throw new IllegalArgumentException(msg); } obj = point.proceed(); return obj;}The core verification method In the doCheck method, the main principle is to obtain the field name and verification rules specified on the annotation, obtain the value of the corresponding field in the parameter entity through reflection, and then perform verification:
/** * Parameter verification* * @param point ProceedingJoinPoint * @return Error message*/private String doCheck(ProceedingJoinPoint point) { // Get method parameter value 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) { // Parsing field FieldInfo info = resolveField(field, methodInfo); // Get the value of the field Object value = ReflectionUtil.invokeGetter(vo, info.field); // Execute the verification rule Boolean isValid = info.optEnum.fun.apply(value, info.operatorNum); msg = isValid ? msg : info.innerMsg; } } } return msg;}You can see that the main logic is:
Parse fields -> Get the value of fields -> Execute verification rules
An enumeration class is maintained internally, and the relevant verification operations are specified in it:
/** * Operation enum */enum Operator { /** * Greater than */ GREATER_THAN(">", CheckParamAspect::isGreaterThan), /** * Greater than or equal to */ GREATER_THAN_EQUAL(">=", CheckParamAspect::isGreaterThanEqual), /** * Less than */ LESS_THAN("<", CheckParamAspect::isLessThan), /** * Less than or equal to */ LESS_THAN_EQUAL("<=", CheckParamAspect::isLessThanEqual), /** * Not equal to */ NOT_EQUAL("!=", CheckParamAspect::isNotEqual), /** * Not empty*/ 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; }} Due to space reasons, I will not expand all the codes one by one. Interested friends can get all the source codes at the following address: ciphermagic/java-learn/sandbox/checker
TODO
at last
Thank you for your reading. Friends who like it can like it on github. If you have any questions or suggestions, please leave a message below and look forward to your reply.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.