Java Bean Validation注解效验详解及常见UnexpectedTypeException

介绍:

在项目开发中,实体参数效验经常用到,例如在导入,参数传递,不仅仅是数据安全,还是保证系统的健壮性.

后端参数校验通常是是直接在业务方法里面进行逻辑判断,执行具体的业务。但这样带给我们的是代码的耦合,冗余。当我们多个地方需要校验时,我们就需要在每一个地方调用校验程序,导致代码很冗余,且不美观。

在做导入时,效验表格实体时应用到了Hibernate Validator 校验工具,Hibernate validator 在JSR303的基础上对校验注解进行了扩展,如果效验注解仍不满足业务的效验规则还可以自定义校验注解。效验注解如下

@NotNull 和 @NotEmpty  和@NotBlank 区别

@NotEmpty 用在集合类上面
@NotBlank 用在String上面
@NotNull    用在基本类型,包装类型上

Java Bean Validation注解效验详解及常见UnexpectedTypeException_第1张图片

扩展注解: 

 Java Bean Validation注解效验详解及常见UnexpectedTypeException_第2张图片

 

先引入依赖



    javax.validation
    validation-api
    2.0.1.Final


    org.hibernate.validator
    hibernate-validator
    6.0.7.Final

示例代码
Ctroller层

import com.ahgj.community.canal.bean.dao.PortBaseDao;
import com.ahgj.community.canal.request.validator.BasicInfoValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
@RestController
@RequestMapping(value = "/configPortBase")
public class ConfigPortBaseController {
    private static final Logger logger = LoggerFactory.getLogger(ConfigPortBaseController.class);
    /**
     * 注册校验器
     */
    @InitBinder
    public void initBinder(DataBinder binder){
        // 这个方法加载验证器,判断请求过来的要验证的对象,加载相对应的验证器。此方法是根据请求加载的,即n次请求就加载n次该方法。
        if (binder.getTarget() != null) {
            if(basicInfoValidator.supports(binder.getTarget().getClass())) {
                binder.addValidators(basicInfoValidator);
            }
        }
    }
    @Autowired
    private BasicInfoValidator basicInfoValidator;

    /**
     * 
     * 
?请求地址: /communityCanal/configPortBase/addPortInfo *
?: *
?param baseDao 端基础配置信息 *
?return */ @RequestMapping(value = "/addPortInfo") public String addPortInfo(@RequestParam Map map, @Valid PortBaseDao baseDao) { }

注入的效验器

import com.ahgj.community.canal.bean.dao.PortBaseDao;
import com.ahgj.community.canal.mapper.PortBaseInfoMapper;
import com.ahgj.community.canal.utils.SpringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

/**
 * 多端同步配置信息效验
 *
 * @author Hohn
 */
@Component
public class BasicInfoValidator implements Validator {

    @Override
    public boolean supports(Class classz) {
        // 判断是否是要校验的类,这里是PortBaseDao.
        return PortBaseDao.class.equals(classz);
    }

    @Override
    public void validate(Object o, Errors errors) {
        PortBaseDao basicInfo = (PortBaseDao) o;
        PortBaseInfoMapper baseInfoMapper = SpringUtils.getBean(PortBaseInfoMapper.class);
        //=====================================================
        // 根据业务实现判断,这里是验证唯一性
        //=====================================================
        boolean result = true;
        try {
            result = baseInfoMapper.isSynchronizationTableExist(basicInfo.getTargetTableName()) > 0 ? true : false;
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(result) {
            errors.rejectValue("targetTableName", HttpStatus.BAD_REQUEST.toString(), null, "#######################");
        }
    }

}

相关bean和工具方法

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
/**
 * Spring Context工具类.
 *
 * @author Hohn
 */
@Component
@Scope("singleton")
public class SpringUtils implements ApplicationContextAware {

    /**
     * Spring应用上下文环境.
     */
    private static ApplicationContext applicationContext;

    /**
     * 实现ApplicationContextAware接口的回调方法,设置上下文环境
     *
     * 
?param: applicationContext * @throws BeansException */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringUtils.applicationContext = applicationContext; } /** * 获取ApplicationContext. * *
?return: ApplicationContext */ public static ApplicationContext getApplicationContext() { return applicationContext; } /** * 获取对象. * *
?param: name *
?return: Object 一个以所给名字注册的bean的实例 * @throws BeansException */ public static T getBean(String name) throws BeansException { return (T) applicationContext.getBean(name); } /** * 获取类型为requiredType的对象. * *
?param: clz *
?return: * @throws BeansException */ public static T getBean(Class clz) throws BeansException { return (T)applicationContext.getBean(clz); }

}

 

import lombok.Builder;
import lombok.Data;
import org.hibernate.validator.constraints.Range;

import javax.persistence.Column;
import javax.persistence.Table;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;


@Data
@Builder
@Table(name = "C_PORT_BASE")
public class PortBaseDao implements Serializable {
    @Column(name = "ID")
    private String id;
    /**
     * 数据端名称
     */
    @NotBlank(message = "数据端名称不能为空")
    @Column(name = "PORT_NAME")
    private String portName;

    /**
     * 目标表名
     */
    @NotBlank(message = "目标表名不能为空")
    @Column(name = "TARGET_TABLE_NAME")
    private String targetTableName;

    /**
     * 目标表字段
     */
    @NotBlank(message = "目标表字段不能为空")
    @Column(name = "TARGET_TABLE_FIELD")
    private String targetTableField;
    /**
     * 增量更新字段
     */
    @NotBlank(message = "同步的表名不能为空")
    @Column(name = "UPDATE_FIELD")
    private String updateField;
    /**
     * 分批批量插入执行条数
     */
    @NotNull(message = "分批批量插入执行条数不能为空")
    @Column(name = "EXECUTIONS_NUMBER")
    private Integer executionsNumber;

    /**
     * 同步类型 1: 全量同步 2: 增量同步
     */
    @Column(name = "SYNCHRONIZE_TYPE")
    private Integer synchronizeType;
    /**
     * 连接类型(同步方式) 1: oracle 2: mysql 3: http/https协议 4: webSocket协议
     */
    @Range(min = 1, max = 5, message = "请选择正确的同步方式")
    @Column(name = "SYNCHRONIZE_WAY")
    private Integer synchronizeWay;

    /**
     * 定时任务时间
     */
    @Column(name = "CRON")
    private String cron;

    /**
     * 删除状态(0未删除、1已删除,默认0")
     */
    @Column(name = "DELETE_FLAG")
    private String deleteFlag;

}

异常捕获处理类

import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 全局异常统一处理.
 * ControllerAdvice注解扫描基础包名,如后期引入其他包需要添加基础包名
 *
 * @author:Hohn
 */
@ControllerAdvice(basePackages = {"com.ahgj.*"})
public class GlobalExceptionHandler {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        StringTrimmerEditor stringTrimmerEditor = new StringTrimmerEditor(true);
        binder.registerCustomEditor(String.class, stringTrimmerEditor);
    }

    /**
     * BindException处理
     * org.springframework.validation.Validator、
     * org.hibernate.validator、javax.validation错误.
     * 

*
?param: request 请求. *
?param: bindException bindException. *
?return: Map. * * @throws Exception */ @ExceptionHandler(value = BindException.class) @ResponseBody public Map validatorErrorHandler(BindException bindException) throws Exception { Map map = new HashMap(2); List errors = bindException.getBindingResult().getFieldErrors(); String message = null; for (int i = 0, size = errors.size(); i < size; i++) { FieldError fieldError = errors.get(i); message = fieldError.getDefaultMessage(); } map.put("status", HttpServletResponse.SC_BAD_REQUEST); map.put("message", message); return map; } /** * 处理系统自定义异常错误. *

*
?param: request 请求. *
?param: informationException. *
?return: Map. * * @throws Exception */ @ExceptionHandler(value = GjSystemException.class) @ResponseBody public Map ErrorHandler(GjSystemException systemException) throws Exception { Map map = new HashMap(2); map.put("status", HttpServletResponse.SC_BAD_REQUEST); map.put("message", systemException.getMessage()); return map; } /** * 处理系统异常错误. *

*
?param: request 请求. *
?param: informationException. *
?return: Map. * * @throws Exception */ @ExceptionHandler(value = Exception.class) @ResponseBody public Map ErrorHandler(Exception exception) throws Exception { Map map = new HashMap(2); map.put("status", HttpServletResponse.SC_INTERNAL_SERVER_ERROR); map.put("message", exception.getMessage()); return map; } }

import lombok.Data;

/**
 * 系统异常类
 *
 * @author:Hohn
 */
@Data
public class GjSystemException extends RuntimeException {
   private static final long serialVersionUID = 1L;
   private int code;

   public GjSystemException(String message) {
      super(message);
   }

   public GjSystemException(String message, Throwable cause) {
      super(message, cause);
   }

   public GjSystemException(String message, int status) {
      super(message);
      this.code = status;
   }

}

 

你可能感兴趣的:(Spring应用实践)