介绍:
在项目开发中,实体参数效验经常用到,例如在导入,参数传递,不仅仅是数据安全,还是保证系统的健壮性.
后端参数校验通常是是直接在业务方法里面进行逻辑判断,执行具体的业务。但这样带给我们的是代码的耦合,冗余。当我们多个地方需要校验时,我们就需要在每一个地方调用校验程序,导致代码很冗余,且不美观。
在做导入时,效验表格实体时应用到了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; } }