Implementing Method-Level Parameter Validation with Custom Annotations in SpringBoot
This article demonstrates how to create a reusable, method‑level parameter validation mechanism in SpringBoot using custom @Check annotations, covering the motivation, usage examples, Maven dependencies, annotation definition, core AOP logic, validation algorithm, and future enhancements.
This article introduces a generic interface parameter validation solution built on SpringBoot and JDK8, leveraging custom annotations to perform method‑level checks.
Motivation
Typical validation adds annotations to entity classes, which works only when the same rules apply to all methods. For different business scenarios—e.g., a user registration requiring both name and age, while login only needs name—class‑level validation is unsuitable. Therefore a method‑level validation approach is needed.
Usage Example
Consider the following AccountVO entity:
public class AccountVO {
private String name; // 姓名
private Integer age; // 年龄
}In a service implementation, the @Check annotation specifies which fields must be validated for a particular method:
@Service
public class TestImpl implements ITestService {
@Override
@Check({"name", "age"})
public void testValid(AccountVO vo) {
// ...
}
}The annotation can also express more complex rules such as non‑null, size limits, or equality checks: @Check({"id>=8", "name!=aaa", "title<10"}) When validation fails, a default error message is generated, e.g.:
updateUserId must not null while calling testValid
id must >=8 while calling testValid
name must != aaa while calling testValidCustom error messages can be provided by appending a colon and the message:
@Check({"title<=8:标题字数不超过8个字,含标点符号"})Maven Dependencies
Besides the SpringBoot starter, the following third‑party libraries are required:
<!-- 用于字符串校验 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<!-- 用于日志打印 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>Custom Annotation Definition
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Parameter validation annotation
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RUNTIME)
public @interface Check {
/**
* Validation rules: fieldName+operator+value[:customMessage]
*/
String[] value();
}Core AOP Logic
The aspect intercepts methods annotated with @Check and performs validation before method execution. If any rule fails, an IllegalArgumentException is thrown.
@Around(value = "@com.cipher.checker.Check")
public Object check(ProceedingJoinPoint point) throws Throwable {
String msg = doCheck(point);
if (!StringUtils.isEmpty(msg)) {
throw new IllegalArgumentException(msg);
}
Object obj = point.proceed();
return obj;
}Validation Method (doCheck)
The doCheck method extracts the method, its @Check annotation, the target object, and iterates over each field rule to evaluate it via reflection.
private String doCheck(ProceedingJoinPoint point) {
Object[] arguments = point.getArgs();
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) {
FieldInfo info = resolveField(field, methodInfo);
Object value = ReflectionUtil.invokeGetter(vo, info.field);
Boolean isValid = info.optEnum.fun.apply(value, info.operatorNum);
msg = isValid ? msg : info.innerMsg;
}
}
}
return msg;
}Operator Enum
The enum defines supported operators (>, >=, <, <=, !=, not null) and associates each with a validation function.
enum Operator {
GREATER_THAN(">", CheckParamAspect::isGreaterThan),
GREATER_THAN_EQUAL(">=", CheckParamAspect::isGreaterThanEqual),
LESS_THAN("<", CheckParamAspect::isLessThan),
LESS_THAN_EQUAL("<=", CheckParamAspect::isLessThanEqual),
NOT_EQUAL("!=", CheckParamAspect::isNotEqual),
NOT_NULL("not null", CheckParamAspect::isNotNull);
// constructor and fields omitted for brevity
}TODO
Package the solution as a Spring Boot Starter component.
Add support for regular‑expression based validation.
Author: cipher – Original article
Source code: GitHub repository
Recent Technical Articles
SpringBoot integration with Activiti workflow (with source)
Deep dive into SpringBoot + Spring Kafka integration
Alibaba's 29 impressive open‑source projects
Animated demo of JDK8 data structures
Gracefully stop a SpringBoot service without kill -9
Lightweight message queue with RedisTemplate
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
