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

目录
  • 通过切面,实现超灵活的注解式数据校验
    • 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();
                }
            }
        }
    }
}

使用

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • spring boot输入数据校验(validation)的实现过程

    项目内容 实现一个简单的用户注册接口,演示怎样进行数据校验. 要求 JDK1.8或更新版本 Eclipse开发环境 如没有开发环境,可参考 [spring boot 开发环境搭建(Eclipse)]. 项目创建 创建spring boot项目 打开Eclipse,创建spring boot的spring starter project项目,选择菜单:File > New > Project ...,弹出对话框,选择:Spring Boot > Spring Starter Project

  • Spring Boot 通过注解实现数据校验的方法

    一.依赖 <!--https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> &l

  • SpringBoot @Validated注解实现参数分组校验的方法实例

    前言 在前后端分离开发的时候我们需要用到参数校验,前端需要进行参数校验,后端接口同样的也需要,以防传入不合法的数据. 1.首先还是先导包,导入pom文件. <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> 2.解释一下注解的作用 @N

  • 详解Spring AOP 实现“切面式”valid校验

    why: 为什么要用aop实现校验? answer: spring mvc 默认自带的校验机制 @Valid + BindingResult, 但这种默认实现都得在Controller方法的中去接收BindingResult,从而进行校验. eg: if (result.hasErrors()) { List<ObjectError> allErrors = result.getAllErrors(); List<String> errorlists = new ArrayList

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

    目录 通过切面,实现超灵活的注解式数据校验 Spring MVC的校验方式 通过切面实现自己的注解式数据校验 Spring boot aop注解数据权限校验 注解类 AOP切面 使用 通过切面,实现超灵活的注解式数据校验 在企业系统的开发中,用户表单输入的场景是会经常遇见的,如何让数据校验脱离于业务代码逻辑,谁也不想在逻辑代码里对字段逐一判断.... Spring MVC的校验方式 在使用Spring MVC时的时候,直接使用hibernate-validator的注解,如下: public c

  • Spring Boot 日志配置方法(超详细)

    默认日志 Logback : 默认情况下,Spring Boot会用Logback来记录日志,并用INFO级别输出到控制台.在运行应用程序和其他例子时,你应该已经看到很多INFO级别的日志了. 从上图可以看到,日志输出内容元素具体如下: 时间日期:精确到毫秒 日志级别:ERROR, WARN, INFO, DEBUG or TRACE 进程ID 分隔符:- 标识实际日志的开始 线程名:方括号括起来(可能会截断控制台输出) Logger名:通常使用源代码的类名 日志内容 添加日志依赖 假如mave

  • Spring Boot 2.4 新特性之一键构建Docker镜像的过程详解

    背景 在我们开发过程中为了支持 Docker 容器化,一般使用 Maven 编译打包然后生成镜像,能够大大提供上线效率,同时能够快速动态扩容,快速回滚,着实很方便.docker-maven-plugin 插件就是为了帮助我们在 Maven 工程中,通过简单的配置,自动生成镜像并推送到仓库中. spotify .fabric8 这里主要使用的主要是如下两种插件 spotify .fabric8 , - -配置通过 xml 定义出 Dockerfile 或者挂载外部 Dockerfile 通过调用

  • 详解spring注解式参数校验

    一般入参我们都会转为vo对象.那么直接在对象的属性上注解即可. 其实spring用的是hibernate的validator. 步骤 1.配置spring.xml <mvc:annotation-driven /> 2.配置自己的validate类. <bean id="validateArgsAOP" class="com.my.validate.aop.ValidateArgsAOP"/> <aop:config> <a

  • 超详细的Spring Boot入门笔记(总结)

    1. Spring Boot 入门 Spring Boot是Spring社区较新的一个项目.该项目的目的是帮助开发者更容易的创建基于Spring的应用程序和服务,让更多人的人更快的对Spring进行入门体验,让Java开发也能够实现Ruby on Rails那样的生产效率.为Spring生态系统提供了一种固定的.约定优于配置风格的框架. Spring Boot具有如下特性: 为基于Spring的开发提供更快的入门体验 开箱即用,没有代码生成,也无需XML配置.同时也可以修改默认值来满足特定的需求

  • spring boot Logging的配置以及使用详解

    前言:该篇文章基本上是翻译的官方文档! spring boot使用Commons Logging作为内部的日志系统,并且给Java Util Logging,Log4J2以及Logback都提供了默认的配置.如果使用了spring boot的Starters,那么默认会使用Logback用于记录日志. 一.Log format spring boot中默认的日志输出格式如下: 2014-03-05 10:57:51.112 INFO 45469 --- [ main] org.apache.ca

  • 详解使用Spring Boot的AOP处理自定义注解

    上一篇文章Java 注解介绍讲解了下Java注解的基本使用方式,并且通过自定义注解实现了一个简单的测试工具:本篇文章将介绍如何使用Spring Boot的AOP来简化处理自定义注解,并将通过实现一个简单的方法执行时间统计工具为样例来讲解这些内容. AOP概念 面向侧面的程序设计(aspect-oriented programming,AOP,又译作面向方面的程序设计.观点导向编程.剖面导向程序设计)是计算机科学中的一个术语,指一种程序设计范型.该范型以一种称为侧面(aspect,又译作方面)的语

  • 浅谈Spring Boot日志框架实践

    Java应用中,日志一般分为以下5个级别: ERROR 错误信息 WARN 警告信息 INFO 一般信息 DEBUG 调试信息 TRACE 跟踪信息 Spring Boot使用Apache的Commons Logging作为内部的日志框架,其仅仅是一个日志接口,在实际应用中需要为该接口来指定相应的日志实现. SpringBt默认的日志实现是Java Util Logging,是JDK自带的日志包,此外SpringBt当然也支持Log4J.Logback这类很流行的日志实现. 统一将上面这些 日志

  • Java经典面试题汇总:Spring Boot

    目录 1. 什么是 Spring Boot? 2. 为什么要用 Spring Boot? 3. Spring Boot 核心配置文件是什么? 4. Spring Boot 提供了哪些常用的 Starter Project Options? 5. 如何快速构建一个Spring Boot项目? 6. Spring Boot默认的内置Web服务器是什么? 7. Spring Boot常用注解及其作用? 8. 如何使用配置文件通过 Spring Boot 配置特定环境的配置? 总结 1. 什么是 Spr

随机推荐