介绍:
在项目开发中,实体参数效验经常用到,例如在导入,参数传递,不仅仅是数据安全,还是保证系统的健壮性.
后端参数校验通常是是直接在业务方法里面进行逻辑判断,执行具体的业务。但这样带给我们的是代码的耦合,冗余。当我们多个地方需要校验时,我们就需要在每一个地方调用校验程序,导致代码很冗余,且不美观。
在做导入时,效验表格实体时应用到了Hibernate Validator 校验工具,Hibernate validator 在JSR303的基础上对校验注解进行了扩展,如果效验注解仍不满足业务的效验规则还可以自定义校验注解。效验注解如下
@NotNull 和 @NotEmpty 和@NotBlank 区别
@NotEmpty 用在集合类上面
@NotBlank 用在String上面
@NotNull 用在基本类型,包装类型上
扩展注解:
先引入依赖
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 Mapmap, @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 staticT getBean(String name) throws BeansException { return (T) applicationContext.getBean(name); } /** * 获取类型为requiredType的对象. * *
?param: clz *
?return: * @throws BeansException */ public staticT 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 MapvalidatorErrorHandler(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 MapErrorHandler(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 MapErrorHandler(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; } }