乐趣区

关于springboot:SpringBoot2-参数管理实践入参出参与校验

一、参数治理

在编程零碎中,为了能写出良好的代码,会依据是各种设计模式、准则、束缚等去标准代码,从而进步代码的可读性、复用性、可批改,实际上集体感觉,如果写出的代码很好,即他人批改也无奈毁坏原作者的思路和封装,这应该是十分高水准。

然而在日常开发中,碍于很多客观因素,很少有工夫去一直思考和优化代码,所以只能从理论状况的角度去思考如何构建零碎代码,保障当前本人还能读懂本人的代码,在本人的几年编程中,理论会思考如下几个方面:代码层级治理,命名和正文对立,正当的设计业务数据库,明确参数格调。

这里就来聊一下参数治理,围绕:入参、校验、返参三个方面内容。

如何了解代码标准这个概念:即大多数开发认同,违心恪守的束缚,例如 Spring 框架和 Mvc 模式对于工程的治理,《Java 开发手册》中对于业务开发的规定,其基本目标都是想防止随着业务倒退,代码演变到无奈保护的境界。

二、接管参数

接管参数形式有很多种,List,Map,Object 等等,然而为了明确参数的语义,通常都须要设计参数对象的构造并且恪守肯定的标准,例如明确禁止 Map 接管参数:

Rest 格调接管单个 ID 参数:

@GetMapping("/param/single/{id}")
public String paramSingle (@PathVariable Integer id){return "Resp:"+id ;}

接管多个指定的参数:

@GetMapping("/param/multi")
public String paramMulti (@RequestParam("key") String key, @RequestParam("var") String var){return "Resp:"+key+var ;}

基于 Java 包装对象入参:

@PostMapping("/param/wrap")
public ParamIn paramWrap (@RequestBody ParamIn paramIn){return paramIn ;}

-- 参数对象实体
public class ParamIn {
    private Integer id ;
    private String key ;
    private String var ;
    private String name ;
}

以上是在开发中罕用的几种接参形式,这里通常会恪守上面几个习惯:

  • 参数语义:明确接管参数的作用;
  • 个数限度:参数超过三个应用包装对象;
  • 防止多个接口应用单个包装对象入参;
  • 防止包装对象主体过于简单;

参数接管并没有很简单的束缚,整体上也比拟容易恪守,通常的问题在于解决较大主体对象时,容易产生一个包装对象被多处复用,进而导致对象字段属性很多,这种状况在简单业务中尤其容易呈现,这种对象并不利于 web 层接口应用,或者很多时候都会在业务层和接口层混用对象;

在业务层封装简单的 BO 对象来升高业务管理的复杂度,这是正当常见的操作,能够在 web 接口层面依据接口性能各自治理入参主体,在业务实现的过程中,再传入 BO 对象中。

防止简单的业务包装对象在各个层乱飘,如果多个接口入参都是同一个简单的对象,很容易让开发人员迷茫。

三、响应参数

与参数接管绝对应的就是参数响应,参数响应通常具备明确的束缚标准:响应主体数据,响应码,形容信息。通常来说就是这样三个外围因素。

响应参数主体:

这里泛型的应用通常用来做主体数据的接管。

public class Resp<T> {

    private int code ;
    private String msg ;
    private T data ;

    public static <T> Resp<T> ok (T data) {Resp<T> result = new Resp<>(HttpStatus.OK);
        result.setData(data);
        return result ;
    }

    public Resp (HttpStatus httpStatus) {this.code = httpStatus.value();
        this.msg = httpStatus.getReasonPhrase();}

    public Resp(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
}

Code 状态码

即接口状态,倡议参照并恪守 HttpStatus 中状态码的形容,这是开发广泛恪守的标准,如果不满足业务需要,在适当自定义局部编码,能够齐全自定义一套响应码,然而没太多必要。

Msg 形容

形容接口的响应的 Msg 可能就是:胜利或失败,更多的时候是须要解决业务异样的提示信息,例如单号不存在,账号解冻等等,通常须要从业务异样中捕捉提示信息,并响应页面,或者入参校验不通过的形容。

Data 数据

接口响应的主体数据,不同的业务响应的对象必定不同,所以这里基于泛型机制接管即可,再以 JSON 格局响应页面。

参考案例

接口返参:

@PostMapping("/resp/wrap")
public Resp<KeyValue> respWrap (@RequestBody KeyValue keyValue){return Resp.ok(keyValue) ;
}

响应格局:

{
   "code": 200,
   "msg": "OK",
   "data": {
       "key": "hello",
       "value": "world"
   }
}

四、参数校验

参数接管和响应绝对都不是简单的,比拟难解决的就是参数校验:入参束缚校验,业务合法性校验,响应参数非空非 null 校验,等各种场景。

在零碎运行过程中,任何参数都不是相对牢靠的,所以参数校验随处可见,不同场景下的参数校验,都有其必要性,但其基本目标都是为了给到申请端提示信息,疾速打断流程,疾速响应。

1、借鉴参考

很多封装思维,设计模式,或者这里说的参数校验,都能够参考现有 Java 源码或者优良的框架,这是一个应该具备的根底意识。

Java 原生办法之 java.lang.Thread 线程:

public void interrupt() {if (this != Thread.currentThread())
        checkAccess();
    synchronized (blockerLock) {
        Interruptible b = blocker;
        if (b != null) {interrupt0();   
            b.interrupt(this);
            return;
        }
    }
    interrupt0();}

在 Java 源码中,大部分都是采纳原生的 if 判断形式,对参数执行校验

Spring 框架 org.springframework.util.ClassUtils工具类局部代码:

public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
            throws ClassNotFoundException, LinkageError {Assert.notNull(name, "Name must not be null");
        Class<?> clazz = resolvePrimitiveClassName(name);
        if (clazz == null) {clazz = commonClassCache.get(name);
        }
        if (clazz != null) {return clazz;}
}

在 Spring 框架中除了根底的 if 判断之外,还封装一个 org.springframework.util.Assert 断言工具类。

2、罕用校验形式

If 判断

@GetMapping("/check/base")
public String baseCheck (@RequestParam("var") String var){if (var == null) {return var+"is null" ;}
    if ("".equals(var)){return var+"is empty" ;}
    if ("hello".equals(var)){return var+"sensitive word" ;}
    return var + "through" ;
}

这种判断在代码中很常见,只是一旦遇到校验的主体对象很大,并且在分布式的环境中,须要反复写 if 判断的话,容易出错是一个方面,对开发人员的急躁考验是另一个方面。

Valid 组件

在早几年的时候,比拟风行的罕用校验组件Hibernate-Validator,起初衰亡的Validation-Api,据说是参考前者实现,不过这并不重要,二者都简化了对 JavaBean 的校验机制。

基于注解的形式,标记 Java 对象的字段属性,并设定如果校验失败的提示信息。

public class JavaValid {@NotNull(message="ID 不能为空")
    private Integer id ;

    @Email(message="邮箱格局异样")
    private String email ;

    @NotEmpty(message = "字段不能为空")
    @Size(min = 2,max = 10,message = "字段长度不合理")
    private String data ;
}

校验后果打印:

public class JavaValidTest {

    private static Validator validator ;

    @BeforeClass
    public static void beforeBuild (){validator = Validation.buildDefaultValidatorFactory().getValidator();}

    @Test
    public void checkValid (){JavaValid valid = new JavaValid(null,"email","data") ;
        Set<ConstraintViolation<JavaValid>> validateInfo = validator.validate(valid) ;
        // 打印校验后果
        validateInfo.stream().forEach(validObj -> {System.out.println("validateInfo:"+validObj.getMessage());
        });
    }
}

接口应用:

@PostMapping("/java/valid")
public JavaValid javaValid (@RequestBody @Valid JavaValid javaValid,BindingResult errorMsg){if (errorMsg.hasErrors()){List<ObjectError> objectErrors = errorMsg.getAllErrors() ;
        objectErrors.stream().forEach(objectError -> {logger.info("CheckRes:{}",objectError.getDefaultMessage());
        });
    }
    return javaValid ;
}

这种校验机制基于注解形式,能够大幅度简化一般的入参校验,然而对业务参数的非法校验并不适应,例如常见的 ID 不存在,状态拦挡等。

Assert 断言

对于 Assert 断言形式,起初是在单元测试中常见,起初在各种优良的框架中开始常见,例如 Spring、Mybatis 等,而后就开始呈现在业务代码中:

public class AssertTest {
    private String varObject ;
    @Before
    public void before (){varObject = RandomUtil.randomString(6) ;
    }

    @Test
    public void testEquals (){Assert.assertEquals(varObject+"不匹配",varObject,RandomUtil.randomString(6));
    }
    @Test
    public void testEmpty (){Assert.assertTrue(StrUtil.isNotEmpty(varObject));
        Assert.assertFalse(varObject+"not empty",StrUtil.isNotEmpty(varObject));
    }
    @Test
    public void testArray (){
        /*
            数组元素不相等: arrays first differed at element [1];
            Expected :u08
            Actual   :mwm
         */
        String var = RandomUtil.randomString(5) ;
        String[] arrOne = new String[]{var,RandomUtil.randomString(3)} ;
        String[] arrTwo = new String[]{var,RandomUtil.randomString(3)} ;
        Assert.assertArrayEquals("数组元素不相等",arrOne,arrTwo);
    }
}

Assert 断言,能够替换传统的 if 判断,大量缩小参数校验的代码行数,进步程序的可读性,这种格调是目前比拟风行的形式。

五、源代码地址

GitHub·地址
https://github.com/cicadasmile/middle-ware-parent
GitEE·地址
https://gitee.com/cicadasmile/middle-ware-parent

浏览标签

【Java 根底】【设计模式】【构造与算法】【Linux 零碎】【数据库】

【分布式架构】【微服务】【大数据组件】【SpringBoot 进阶】【Spring&Boot 根底】

【数据分析】【技术导图】【职场】

退出移动版