Spring boot通过切面,实现超灵活的注解式数据校验过程

作者:Balalalalalalalala 时间:2022-11-29 07:40:08 

目录
  • 通过切面,实现超灵活的注解式数据校验

    • Spring MVC的校验方式

    • 通过切面实现自己的注解式数据校验

  • Spring boot aop注解数据权限校验

    • 注解类

    • AOP切面

    • 使用

通过切面,实现超灵活的注解式数据校验

在企业系统的开发中,用户表单输入的场景是会经常遇见的,如何让数据校验脱离于业务代码逻辑,谁也不想在逻辑代码里对字段逐一判断。。。。

Spring MVC的校验方式

在使用Spring MVC时的时候,直接使用hibernate-validator的注解,如下:


public class User {
   private Long id;
   @NotBlank(message = "name不能为空")
   @Size(min = 5, max = 10, message = "字符在5到10个")
   private String name;
   private String des;
   @NotNull
   @Max(value = 3, message = "type 参数错误")
   @Min(value = 0, message = "type 参数错误")
   private Integer type;
   @Min(value = 0, message = "参数错误, limit必须大于或等于0")  
   private int limit;
   @Pattern(regexp = "^(true|false)$", message = "参数错误, 参数isActive只能是true或者false")
   private String flag;
   // setters and getters

然后将User对象作为Controller的参数,交给Spring MVC去帮你校验。

通过切面实现自己的注解式数据校验

这是一个SOA的微服务应用,没有controller和Spring MVC,当然也没有所谓的容器(Tomcat、Jetty),对于来自于client的调用,也要进行参数校验。继续基于hibernate-validator,

参看validator的官方文档

引入依赖:


<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator-cdi</artifactId>
  <version>5.4.1.Final</version>
</dependency>
<dependency>
  <groupId>org.glassfish</groupId>
  <artifactId>javax.el</artifactId>
  <version>3.0.1-b08</version>
</dependency>

这里需要引入spring boot和aop的一些知识点,自行去网上google吧。我直接上代码了,谁叫我是代码的搬运工。

定义一个切面:


@Aspect  //一个切面
@Configuration  // spring boot 配置类
public class RequestParamValidAspect {
   private final ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
   private final ExecutableValidator methodValidator = factory.getValidator().forExecutables();
   private final Validator beanValidator = factory.getValidator();
   private <T> Set<ConstraintViolation<T>> validMethodParams(T obj, Method method, Object [] params){
       return methodValidator.validateParameters(obj, method, params);
   }
   private <T> Set<ConstraintViolation<T>> validBeanParams(T bean) {
       return beanValidator.validate(bean);
   }
   @Pointcut("execution(* com.jiaobuchong.commodity.service.*.*(..))")
   public void soaServiceBefore(){}
   /* * 通过连接点切入 */
   @Before("soaServiceBefore()")
   public void twiceAsOld1(JoinPoint point) {
       //  获得切入目标对象
       Object target = point.getThis();
       // 获得切入方法参数
       Object [] args = point.getArgs();
       // 获得切入的方法
       Method method = ((MethodSignature)point.getSignature()).getMethod();
       // 校验以基本数据类型 为方法参数的
       Set<ConstraintViolation<Object>> validResult = validMethodParams(target, method, args);
       Iterator<ConstraintViolation<Object>> violationIterator = validResult.iterator();
       while (violationIterator.hasNext()) {
           // 此处可以抛个异常提示用户参数输入格式不正确
           System.out.println("method check---------" + violationIterator.next().getMessage());
       }
       // 校验以java bean对象 为方法参数的
       for (Object bean : args) {
           if (null != bean) {
               validResult = validBeanParams(bean);
               violationIterator = validResult.iterator();
               while (violationIterator.hasNext()) {
           // 此处可以抛个异常提示用户参数输入格式不正确
                   System.out.println("bean check-------" + violationIterator.next().getMessage());
               }
           }
       }
   }
}

具体的Service


// DemoService.java
public interface DemoService {
   void one(@NotNull(message = "不能为null") Integer a, @NotBlank String b);
   void two(@NotNull(message = "paramsVo不能为null") ParamsVo paramsVo,
            @NotNull(message = "go不能为null") String go);
}
// ParamsVo.java
public class ParamsVo {
   @NotBlank(message = "不能为空")
   private String name;
   @NotBlank
   @Length(min = 2, max = 20, message = "不可以为空,最多20个字")
   private String desc;
   @NotNull
   @Valid  // 需要加上@Valid注解,不然不会校验到Img对象
   private List<Img> imgList;
   @NotNull(message = "length不能为null")
   @Range(min = 3, max = 100, message = "长度范围3-100")
   private Integer length;
   // omitted other code
}
public class Img {
   @NotNull(message = "img id 不能为null")
   private Long id;
   @NotBlank(message = "img name 不能为空")
   private String name;
   // omitted other code
}

运行DemoService:


   @Autowired
   private DemoService demoService;
   @Test
   public void testGo() {
       demoService.one(null, "");
       ParamsVo paramsVo = new ParamsVo();
       List<Img> list = new ArrayList<>();
       Img img = new Img();
       list.add(img);
       paramsVo.setImgList(list);
       paramsVo.setDesc("你");
       paramsVo.setLength(1);
       demoService.two(paramsVo, null);
   }

运行结果:

method check———不能为空
method check———不能为null
method check———go不能为null
bean check——-img name 不能为空
bean check——-不能为空
bean check——-深度范围3-100
bean check——-img id 不能为null
bean check——-不可以为空,最多20个字

这样比Spring MVC的校验功能还强大了,


// Spring MVC中对下面这样的校验是没有作用的
void one(@NotNull(message = "不能为null") Integer a, @NotBlank String b);

经过一番改造后,啥都支持了。而且独立于业务逻辑,维护和新增校验都很方便,代码量也变少了!

Spring boot aop注解数据权限校验

注解类


@Retention(RetentionPolicy.RUNTIME)
public @interface DataAuthValid
{    
   //位置
   public int index() default 0;

//字段   id
   //public String id() default "id";

//字段   id
   public String orgId() default "org_id";

//mapper
   @SuppressWarnings("rawtypes")
   public Class<? extends Mapper> mapper();
}

AOP切面


@Aspect
@Component
@Order(1)
public class DataAuthAop {
   private static String types = "java.lang.String,java.lang.Long,long";    
   @Before("@annotation(dataAuth)")
   public void beforeMethod(JoinPoint point,DataAuthValid dataAuth) throws Exception {

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
       Map<String, Object> payloadMap = (Map<String, Object>) request.getAttribute("payloadMap");
       Long companyid = Long.parseLong(payloadMap.get("companyid")+"");
       if(companyid != 1) {
           Object[] args = point.getArgs();
           Object obj = args[dataAuth.index()];
           String ids = null;
           String typeName = obj.getClass().getTypeName();
           if(types.contains(typeName)) {
               ids = obj + "";
           }else {
               Field[] fields = obj.getClass().getDeclaredFields();
               for (Field f : fields) {
                   f.setAccessible(true);
                   if("id".equals(f.getName())) {
                       Long id = (Long) f.get(obj);
                       ids = id + "";
                   }
               }
           }
           String[] idArr = ids.split(",");
           for (String id : idArr) {
               Class cla = dataAuth.mapper();
               Mapper mapper = (Mapper) SpringBeanFactoryUtils.getApplicationContext().getBean(cla);
               Object object = mapper.selectByPrimaryKey(Long.valueOf(id));
               Field field = obj.getClass().getDeclaredField(dataAuth.orgId());
               field.setAccessible(true);
               Long orgId = (Long)field.get(obj);
               if(!companyid.equals(orgId)) {
                   throw new RuntimeException();
               }
           }
       }
   }
}

使用

Spring boot通过切面,实现超灵活的注解式数据校验过程

来源:https://blog.csdn.net/jiaobuchong/article/details/74094155

标签:Springboot,切面,注解,数据校验
0
投稿

猜你喜欢

  • Android自定义View实现天气预报折线图

    2023-08-22 14:37:50
  • Java使用Scanner类进行控制台输入实现方法

    2021-08-08 18:34:26
  • 遍历文件系统目录树的深入理解

    2022-09-15 20:03:46
  • Android实现网易新闻客户端侧滑菜单(1)

    2023-09-12 00:54:05
  • 在Android中通过Intent使用Bundle传递对象的使用方法

    2023-04-23 10:15:38
  • Java+Selenium实现文件上传下载功能详解

    2021-06-23 23:33:23
  • Java CompletableFuture 异步超时实现深入研究

    2022-12-28 04:18:14
  • Servlet注解之@WebInitParam多个InitParam的使用

    2023-08-04 13:43:01
  • Android悬浮对话框(即点即关对话框)实现代码

    2023-01-01 04:53:54
  • C# datatable 不能通过已删除的行访问该行的信息处理方法

    2022-07-31 02:57:08
  • Android使用插件实现代码混淆

    2023-02-25 07:13:10
  • 详解SpringBoot2.0的@Cacheable(Redis)缓存失效时间解决方案

    2023-07-23 07:20:54
  • Java中File文件操作类的基础用法

    2022-09-28 14:37:03
  • Java毕业设计实战之医院心理咨询问诊系统的实现

    2022-07-04 19:02:21
  • Android提醒微技巧你真的了解Dialog、Toast和Snackbar吗

    2023-03-08 14:15:44
  • SpringBoot @PostConstruct原理用法解析

    2022-02-02 18:05:46
  • Spring Cloud Eureka 服务上下线监控的实现

    2022-02-18 21:06:15
  • 带着新人看java虚拟机01(推荐)

    2023-05-28 04:38:16
  • Android XML設置屏幕方向(android:screenOrientation)详解

    2021-09-08 09:46:35
  • C++实现优先队列的示例详解

    2022-04-06 22:14:20
  • asp之家 软件编程 m.aspxhome.com