java使用validator进行校验,同时提供日志切面调用

 

参数校验

 在开发中经常需要写一些字段校验的代码,比如字段非空,字段长度限制,格式验证等等,往往大多都在控制层进行每个参数单独判断这样就导致:

  • 验证代码繁琐,重复劳动
  • 方法内代码显得冗长
  • 每次要看哪些参数验证是否完整,需要去翻阅验证逻辑代码

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;
    }
}

猜你喜欢

转载自blog.csdn.net/u012272367/article/details/88800135