在编程系统中,为了能写出良好的代码,会根据是各类设计模式、原则、约束等去规范代码,从而提升代码的可读性、复用性、可修改,实际上我的以为,若是写出的代码很好,即别人修改也没法破坏原做者的思路和封装,这应该是很是高水准。java
可是在平常开发中,碍于不少客观因素,不多有时间去不断思考和优化代码,因此只能从实际状况的角度去思考如何构建系统代码,保证之后本身还能读懂本身的代码,在本身的几年编程中,实际会考虑以下几个方面:代码层级管理,命名和注释统一,合理的设计业务数据库,明确参数风格。git
这里就来聊一下参数管理,围绕:入参、校验、返参三个方面内容。github
如何理解代码规范这个概念:即大多数开发认同,愿意遵照的约束,例如Spring框架和Mvc模式对于工程的管理,《Java开发手册》中对于业务开发的规定,其根本目的都是想避免随着业务发展,代码演变到没法维护的境界。web
接收参数方式有不少种,List,Map,Object等等,可是为了明确参数的语义,一般都须要设计参数对象的结构而且遵照必定的规范,例如明确禁止Map接收参数:算法
Rest风格接收单个ID参数:spring
@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 ; }
以上是在开发中经常使用的几种接参方式,这里一般会遵照下面几个习惯:json
参数接收并无很复杂的约束,总体上也比较容易遵照,一般的问题在于处理较大主体对象时,容易产生一个包装对象被多处复用,进而致使对象字段属性不少,这种状况在复杂业务中尤为容易出现,这种对象并不利于web层接口使用,或者不少时候都会在业务层和接口层混用对象;segmentfault
在业务层封装复杂的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校验,等各类场景。
在系统运行过程当中,任何参数都不是绝对可靠的,因此参数校验随处可见,不一样场景下的参数校验,都有其必要性,但其根本目的都是为了给到请求端提示信息,快速打断流程,快速响应。
不少封装思想,设计模式,或者这里说的参数校验,均可以参考现有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
断言工具类。
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基础】