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

介绍:

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

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

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

@NotNull 和 @NotEmpty  和@NotBlank 区别

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

扩展注解: 

 

 

先引入依赖

<!--jsr 303-->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
<!-- hibernate-validator -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.7.Final</version>
</dependency>

示例代码
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;

    /**
     * 
     * <br>?请求地址: /communityCanal/configPortBase/addPortInfo
     * <br>?:
     * <br>?param  baseDao 端基础配置信息
     * <br>?return   
     */
    @RequestMapping(value = "/addPortInfo")
    public String addPortInfo(@RequestParam Map<String,Object> 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接口的回调方法,设置上下文环境
     *
     * <br>?param: applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringUtils.applicationContext = applicationContext;
    }

    /**
     * 获取ApplicationContext.
     *
     * <br>?return: ApplicationContext
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 获取对象.
     *
     * <br>?param: name
     * <br>?return: Object 一个以所给名字注册的bean的实例
     * @throws BeansException
     */
    public static <T> T getBean(String name) throws BeansException {
        return (T) applicationContext.getBean(name);
    }

    /**
     * 获取类型为requiredType的对象.
     *
     * <br>?param: clz
     * <br>?return:
     * @throws BeansException
     */
    public static <T> T getBean(Class<T> 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错误.
     * <p>
     * <br>?param: request 请求.
     * <br>?param: bindException bindException.
     * <br>?return: Map.
     *
     * @throws Exception
     */
    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public Map<String, Object> validatorErrorHandler(BindException bindException) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>(2);
        List<FieldError> 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;
    }

    /**
     * 处理系统自定义异常错误.
     * <p>
     * <br>?param: request 请求.
     * <br>?param: informationException.
     * <br>?return: Map.
     *
     * @throws Exception
     */
    @ExceptionHandler(value = GjSystemException.class)
    @ResponseBody
    public Map<String, Object> ErrorHandler(GjSystemException systemException) throws Exception {

        Map<String, Object> map = new HashMap<String, Object>(2);
        map.put("status", HttpServletResponse.SC_BAD_REQUEST);
        map.put("message", systemException.getMessage());
        return map;
    }

    /**
     * 处理系统异常错误.
     * <p>
     * <br>?param: request 请求.
     * <br>?param: informationException.
     * <br>?return: Map.
     *
     * @throws Exception
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Map<String, Object> ErrorHandler(Exception exception) throws Exception {

        Map<String, Object> map = new HashMap<String, Object>(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;
   }

}

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值