参数校验
在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,格式验证等等,往往大多都在控制层进行每个参数单独判断这样就导致:
- 验证代码繁琐,重复劳动
- 方法内代码显得冗长
- 每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码
hibernate validator(官方文档)提供了一套比较完善、便捷的验证实现方式。
spring-boot-starter-web
包里面有hibernate-validator
包,不需要引用hibernate validator依赖。
下面用一个简单得demo展示
一、model
public class DemoEntity { private static final long serialVersionUID = 5811831910614660259L; /** 业务商户号 */ @NotBlank @Size(max=32) @Column(name="biz_mer_no") private String bizMerNo; /** 业务商户名 */ @NotBlank @Size(max=64) @Column(name="biz_mer_name") private String bizMerName; /** 业务产品编码 */ @NotBlank @Size(max=8) @Column(name="biz_prod_code") private String bizProdCode; /** 业务产品名称 */ @NotBlank @Size(max=32) @Column(name="biz_prod_name") }
二、简单封装ValidatorUtil /** * JSR303验证工具 * @version v1.4 * @history v1.4-->简化例外属性的判断方式 * @history v1.3-->增加一些常用的pojo配置例子 * @history v1.2-->部分细节优化及增加描述:验证对象若其父类的属性也有验证注解则会一并验证 * @history v1.1-->增加将验证的错误信息存入Map<String,String>后返回的<code>validateToMap()<code>方法 * @history v1.0-->新建 * Created by TOMS. */ public final class ValidatorUtil { private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); private ValidatorUtil() {} /** * 验证对象中的属性的值是否符合注解定义 * @param obj 需要验证的对象,若其父类的属性也有验证注解则会一并验证 * @return 返回空字符串""表示验证通过,否则返回错误信息 */ public static String validate(Object obj){ return validate(obj, new String[]{}); } /** * 验证对象中的属性的值是否符合注解定义 * @param obj 需要验证的对象,若其父类的属性也有验证注解则会一并验证 * @param exceptFieldName 不需要验证的属性 * @return 返回空字符串""表示验证通过,否则返回错误信息 */ public static String validate(Object obj, String... exceptFieldName){ StringBuilder sb = new StringBuilder(); if(null == obj){ return "被验证对象不能为null"; } Set<ConstraintViolation<Object>> validateSet = validator.validate(obj); for(ConstraintViolation<Object> constraintViolation : validateSet){ String field = constraintViolation.getPropertyPath().toString(); String message = constraintViolation.getMessage(); if(!StringUtils.equalsAnyIgnoreCase(field, exceptFieldName)){ //id:最小不能小于1 //name:不能为空 sb.append(field).append(": ").append(message).append("\r\n"); } } if(StringUtils.isNotBlank(sb)){ sb.insert(0, "\r\n"); } return sb.toString(); } /** * 验证对象中的属性的值是否符合注解定义 * @param obj 需要验证的对象,若其父类的属性也有验证注解则会一并验证 * @return 返回空Map<String, String>(not null)表示验证通过,否则会将各错误字段作为key放入Map,value为错误信息 */ public static Map<String, String> validateToMap(Object obj){ return validateToMap(obj, new String[]{}); } /** * 验证对象中的属性的值是否符合注解定义 * @param obj 需要验证的对象,若其父类的属性也有验证注解则会一并验证 * @param exceptFieldName 不需要验证的属性 * @return 返回空Map<String, String>(not null)表示验证通过,否则会将各错误字段作为key放入Map,value为错误信息 */ public static Map<String, String> validateToMap(Object obj, String... exceptFieldName){ Map<String, String> resultMap = Maps.newHashMap(); if(null == obj){ throw new NullPointerException("被验证对象不能为null"); } Set<ConstraintViolation<Object>> validateSet = validator.validate(obj); for(ConstraintViolation<Object> constraintViolation : validateSet){ String field = constraintViolation.getPropertyPath().toString(); String message = constraintViolation.getMessage(); if(!StringUtils.equalsAnyIgnoreCase(field, exceptFieldName)){ resultMap.put(field, message); } } return resultMap; } }
三、切入点
@Configuration
public class LogConfiguration {
@Bean
public LogAdvisor logAdvisor() {
return new LogAdvisor();
}
class LogAdvisor extends AbstractPointcutAdvisor {
private static final long serialVersionUID = 2375671248486443144L;
@Override
public Advice getAdvice() {
return new LogAspect();
}
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut(){
@Override
public boolean matches(Method method, Class<?> targetClass) {
//若类上面注解了DisableLog,则不打印日志(仅此类,不包括该类中被调用的类)
if(targetClass.isAnnotationPresent(DisableLog.class)){
return false;
}
//若方法上面注解了DisableLog,则不打印日志
if(method.isAnnotationPresent(DisableLog.class)){
return false;
}
//若类上面注解了EnableLog,则打印日志
if(targetClass.isAnnotationPresent(EnableLog.class)){
return true;
}
//若方法上面注解了EnableLog,则打印日志
if(method.isAnnotationPresent(EnableLog.class)){
return true;
}
//默认的:打印标注了RestController的类、以及ResponseBody的方法,的日志
return targetClass.isAnnotationPresent(RestController.class) || method.isAnnotationPresent(ResponseBody.class);
}
};
}
}
}
四、切面
只验证方法上带有@EnableFormValid的自定义注解
class LogAspect implements MethodInterceptor {
private static SerializerFeature[] serializerFeatures = new SerializerFeature[5];
static {
serializerFeatures[0] = SerializerFeature.WriteMapNullValue;
serializerFeatures[1] = SerializerFeature.WriteNullListAsEmpty;
serializerFeatures[2] = SerializerFeature.WriteNullNumberAsZero;
serializerFeatures[3] = SerializerFeature.WriteNullStringAsEmpty;
serializerFeatures[4] = SerializerFeature.WriteNullBooleanAsFalse;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object respData = null;
long startTime = System.currentTimeMillis();
//类名.方法名(含类的包名),举例:com.jadyer.mpp.web.MppController.bind
String methodInfo = invocation.getThis().getClass().getName() + "." + invocation.getMethod().getName();
//打印入参(对于入参为MultipartFile类型等,可以在实体类使用@com.alibaba.fastjson.annotation.JSONField(serialize=false)标识不序列化)
Object[] objs = invocation.getArguments();
List<Object> argsList = new ArrayList<>();
for(Object obj : objs){
if(obj instanceof ServletRequest || obj instanceof ServletResponse){
continue;
}
argsList.add(obj);
}
LogUtil.getLogger().info("invoke method {} args is {}", methodInfo, JSON.toJSONStringWithDateFormat(argsList, JSON.DEFFAULT_DATE_FORMAT, serializerFeatures));
//表单验证
for(Object obj : objs){
if(null!=obj && (obj.getClass().isAnnotationPresent(EnableFormValid.class) || invocation.getMethod().isAnnotationPresent(EnableFormValid.class))){
String validateResult = ValidatorUtil.validate(obj);
LogUtil.getLogger().info("{}的表单-->{}", methodInfo, StringUtils.isBlank(validateResult)?"验证通过":"验证未通过");
if (StringUtils.isNotBlank(validateResult)) {
throw new DescribeException(ExceptionEnum.PRAMS_PROCESS_ERROR.getCode(),validateResult);
}
}
}
//执行方法
try {
respData = invocation.proceed();
} catch (Exception e) {
Class<?> targetClass = invocation.getThis().getClass();
Method method = invocation.getMethod();
if (targetClass.isAnnotationPresent(EnableLog.class) || method.isAnnotationPresent(EnableLog.class)) {
LogUtil.getLogger().error("发生异常:Exception Occured,堆栈轨迹如下", e);
}
throw e;
}
//打印出参
long endTime = System.currentTimeMillis();
String returnInfo;
if(null == respData){
returnInfo = "<null>";
}else if(respData instanceof ServletRequest) {
returnInfo = "<javax.servlet.ServletRequest>";
}else if(respData instanceof ServletResponse) {
returnInfo = "<javax.servlet.ServletResponse>";
}else if(respData instanceof InputStream) {
returnInfo = "<java.io.InputStream>";
}else if(respData.getClass().isAssignableFrom(ResponseEntity.class)) {
returnInfo = "<org.springframework.http.ResponseEntity>";
}else{
returnInfo = JSON.toJSONStringWithDateFormat(respData, JSON.DEFFAULT_DATE_FORMAT, serializerFeatures);
}
LogUtil.getLogger().info("invoke method {} result is {} take time {}ms", methodInfo, returnInfo, endTime-startTime);
//LogUtil.getLogger().info("---------------------------------------------------------------------------------------------");
return respData;
}
}