平时使用Hibernate Validator + javax.validation.XXX 进行入参校验,由框架进行自动管理;
但很多情况下我们还是需要动态的手动去校验入参;于是简单写了个工具类;直接调用
AnaValidUtils.validObjectParams(Object object); 即可进行参数校验
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* 功能描述:valid工具类(目前支持属性校验和被@AssertTrue、@AssertFalse修饰的方法校验)
* 当入参存在问题,会抛出异常IllegalArgumentException;未知问题会抛出异常RuntimeException;
* Created by wangpeng on 2019/8/9.
*/
@Slf4j
public class AnaValidUtils {
private volatile static AnaValidUtils uniqueInstance;
private AnaValidUtils() {
}
private static AnaValidUtils getInstance() {
if (uniqueInstance == null) {
synchronized (AnaValidUtils.class) {
if (uniqueInstance == null) {
uniqueInstance = new AnaValidUtils();
}
}
}
return uniqueInstance;
}
/**
* 校验对象参数(当入参存在问题,会抛出异常IllegalArgumentException;未知问题会抛出异常RuntimeException;)
*
* @param request
*/
public static void validObjectParams(Object request) {
AnaValidUtils.getInstance().validateAttributesAndMethods(request);
}
// 校验属性和方法
private void validateAttributesAndMethods(Object request) {
List<Object> toValidObjects = null;
try {
toValidObjects = getAllValidObjects(request, 0);
} catch (IllegalAccessException ex) {
log.error("反射权限问题", ex);
throw new RuntimeException("校验入参失败");
} catch (Exception ex) {
log.error("未知问题", ex);
throw new RuntimeException("校验入参失败");
}
for (Object object : toValidObjects) {
validateAttributes(object);
List<String> validMethodNames = getValidMethodNamesInObject(object);
for (String validMethodName : validMethodNames) {
try {
validateMethodInObject(object, validMethodName);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
log.error("校验入参方法失败", ex);
throw new RuntimeException("校验入参方法失败");
} catch (Exception ex) {
log.error("未知问题", ex);
throw new RuntimeException("校验入参方法失败");
}
}
}
}
// 获取对象中的有效方法
private List<String> getValidMethodNamesInObject(Object object) {
List<String> rtnList = new ArrayList<>();
for (Method method : object.getClass().getMethods()) {
// @AssertTrue 修饰的方法
javax.validation.constraints.AssertTrue annotationAssertTrue = method.getAnnotation(javax.validation.constraints.AssertTrue.class);
if (Objects.nonNull(annotationAssertTrue)) {
String methodName = method.getName();
if (rtnList.contains(methodName)) {
throw new IllegalArgumentException("方法不可重名");
}
rtnList.add(methodName);
}
// @AssertFalse 修饰的方法
javax.validation.constraints.AssertFalse annotationAssertFalse = method.getAnnotation(javax.validation.constraints.AssertFalse.class);
if (Objects.nonNull(annotationAssertFalse)) {
String methodName = method.getName();
if (rtnList.contains(methodName)) {
throw new IllegalArgumentException("方法不可重名");
}
rtnList.add(methodName);
}
}
return rtnList;
}
// 获取所有需要校验的对象(@Valid修饰的属性 + 初始对象)
private List<Object> getAllValidObjects(Object request, Integer level) throws IllegalAccessException {
if (Objects.isNull(request)) {
return new ArrayList<>();
}
level = level + 1;
if (level > 10) {
throw new IllegalArgumentException("对象嵌套过深");
}
// 返回值
List<Object> rtnList = new ArrayList<>();
rtnList.add(request);
for (Field field : request.getClass().getDeclaredFields()) {
//设置对象的访问权限,保证对private的属性的访问
field.setAccessible(true);
javax.validation.Valid annotation = field.getAnnotation(javax.validation.Valid.class);
if (Objects.nonNull(annotation)) {
Object object = field.get(request);
rtnList.addAll(getAllValidObjects(object, level));
}
}
return rtnList;
}
// 校验属性
private void validateAttributes(Object request) {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
//验证某个对象,,其实也可以只验证其中的某一个属性的
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(request);
Iterator<ConstraintViolation<Object>> iter = constraintViolations.iterator();
StringBuilder stringBuilder = new StringBuilder();
while (iter != null && iter.hasNext()) {
stringBuilder.append(iter.next().getMessage()).append(";");
}
if (StringUtils.isNotBlank(stringBuilder.toString())) {
throw new IllegalArgumentException(stringBuilder.toString());
}
}
// 校验对象中的方法
private void validateMethodInObject(Object request, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
//1.获取校验器
Validator validator = factory.getValidator();
//2.获取校验方法参数的校验器
ExecutableValidator validatorParam = validator.forExecutables();
//3.获取要校验的方法
Method method = request.getClass().getMethod(methodName);
Object returnValue = method.invoke(request);//调用方法获取返回值
//4.校验返回值
Set<ConstraintViolation<Object>> constraintViolationSet = validatorParam.validateReturnValue(request, method, returnValue);
StringBuilder stringBuilder = new StringBuilder();
for (ConstraintViolation item : constraintViolationSet) {
stringBuilder.append(item.getMessage()).append(";");
}
if (StringUtils.isNotBlank(stringBuilder.toString())) {
throw new IllegalArgumentException(stringBuilder.toString());
}
}
}
这篇博客介绍了如何在不依赖Hibernate Validator和javax.validation的情况下,动态地进行参数验证。作者提供了一个简单的工具类实现,用于手动校验入参。
1670

被折叠的 条评论
为什么被折叠?



