作者:LeileiChen来源:https://llchen60.com/1.1案例场景假设银行提供了一些API接口,参数的序列化有点特殊。不使用JSON,我们需要将参数一个一个的拼接起来形成一个大字符串:1)按照银行提供的API文档的顺序,将所有参数组成定长数据,拼接在一起作为一个完整的字符串2)因为每个参数都有固定的长度,所以没有达到长度就需要进行填充处理。string类型参数不满足长度的部分用下划线向右填充,即字符串内容不满足number类型参数长度的部分用0到填充左边,也就是实际的数字在右边。货币类型需要将金额向下取整点数输入2位,以点数为单位,左边参数也填写为数字类型做MD5运算作为签名1.2初步代码实现publicclassBankService{//创建用户方法publicstaticStringcreateUser(Stringname,Stringidentity,Stringmobile,intage)throwsIOException{StringBuilderstringBuilder=newStringBuilder();//留下字符串,填充多余的空格_stringBuilder.append(String.format("%-10s",name).replace('','_'));//字符串在左边,多余的空格补上_stringBuilder.append(String.format("%-18s",identity).replace('','_'));//数字在右边,多余的地方补0stringBuilder.append(String.format("%05d",age));//留下字符串,多余的地方用_stringBuilder.append(String.format("%-11s",mobile).replace('','_'));//最后添加MD5作为签名stringBuilder.append(DigestUtils.md2Hex(stringBuilder.toString()));返回Request.Post("http://localhost:45678/reflection/bank/createUser").bodyString(stringBuilder.toString(),ContentType.APPLICATION_JSON).execute().returnContent().asString();}//支付方式publicstaticStringpay(longuserId,BigDecimalamount)throwsIOException{StringBuilderstringBuilder=newStringBuilder();填充0stringBuilder.append(String.format("%020d",userId));//金额向下取整到2位,以分为单位,作为数字向右,多余的地方补0stringBuilder.append(String.format("%010d",amount.setScale(2,RoundingMode.DOWN).multiply(newBigDecimal("100")).longValue()));//最后添加MD5作为签名stringBuilder.append(DigestUtils.md2Hex(stringBuilder.toString()));返回Request.Post("http://localhost:45678/reflection/bank/pay").bodyString(stringBuilder.toString(),ContentType.APPLICATION_JSON)。execute().returnContent().asString();}}这样基本可以满足要求,但是存在一些问题:处理逻辑重复,一不小心,字符串拼接和添加会有bug签发请求的逻辑重复了参数类型和所有方法中实际方法入参的顺序,不一定和接口要求一致,容易出错。代码级别的硬编码参数无法清楚地检查。1.3使用接口和反射优化代码1.3.1实现定义所有接口参数的POJO类@DatapublicclassCreateUserAPI{privateStringname;私人字符串身份;私人字符串移动;privateintage;}1.3.2定义注解本身@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Inheritedpublic@interfaceBankAPI{Stringdesc()default"";字符串url()默认"";}@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)@Documented@Inheritedpublic@interfaceBankAPIField{intorder()默认-1;intlength()默认值-1;Stringtype()default"";}1.3.3反射和注解实现动态接口参数组装privatestaticStringremoteCall(AbstractAPIapi)throwsIOException{//从BankAPI注解获取请求地址BankAPIbankAPI=api.getClass().getAnnotation(BankAPI.class);银行API.url();StringBuilderstringBuilder=newStringBuilderr();Arrays.stream(api.getClass().getDeclaredFields())//获取所有字段.filter(field->field.isAnnotationPresent(BankAPIField.class))//查找标注有annotations的字段.sorted(Comparator.comparingInt(a->a.getAnnotation(BankAPIField.class).order()))//按照注解中的顺序对字段进行排序.peek(field->field.setAccessible(true))//设置可以访问的私有字段.forEach(field->{//获取注解BankAPIFieldbankAPIField=field.getAnnotation(BankAPIField.class);Objectvalue="";try{//反射获取字段值value=field.get(api);}catch(IllegalAccessExceptione){e.printStackTrace();}//使用正确的填充开关根据字段类型格式化字符串(bankAPIField.type()){case"S":{stringBuilder.append(String.format("%-"+bankAPIField.length()+"s",value.toString()).replace('','_'));休息;}case"N":{stringBuilder.append(String.format("%"+bankAPIField.length()+"s",value.toString()).replace('','0'));休息;}case"M":{if(!(valueinstanceofBigDecimal))thrownewRuntimeException(String.format("{}的{}必须是BigDecimal",api,field));stringBuilder.append(String.format("%0"+bankAPIField.length()+"d",((BigDecimal)值).setScale(2,RoundingMode.DOWN).multiply(newBigDecimal("100"))。长值()));休息;默认值:中断;}});//签名递交stringBuilder.append(DigestUtils.md2Hex(stringBuilder.toString()));字符串参数=stringBuilder.toString();长开始=System.currentTimeM伊利斯();//发送请求Stringresult=Request.Post("http://localhost:45678/reflection"+bankAPI.url()).bodyString(param,ContentType.APPLICATION_JSON).execute().returnContent().asString();log.info("调用银行API{}url:{}参数:{}耗时:{}ms",bankAPI.desc(),bankAPI.url(),param,System.currentTimeMillis()-begin);returnresult;}通过反射动态获取类信息,在运行时完成组装过程。在一种方法中,减少了维护中的重复和错误。1.3.4代码中的应用@BankAPI(url="/bank/createUser",desc="CreateUserInterface")@DatapublicclassCreateUserAPIextendsAbstractAPI{@BankAPIField(order=1,type="S",length=10)私有字符串名称;@BankAPIField(order=2,type="S",length=18)privateStringidentity;@BankAPIField(order=4,type="S",length=11)//注意这里的顺序privateStringmobile;@BankAPIField(order=3,type="N",length=5)privateintage;}@BankAPI(url="/bank/pay",desc="paymentinterface")@DatapublicclassPayAPIextendsAbstractAPI{@BankAPIField(order=1,type="N",length=20)privatelonguserId;@BankAPIField(order=2,type="M",length=10)privateBigDecimalamount;}近期热点文章推荐:1.1000+Java面试题及答案(2021最新版)2.不要在if/else满屏,试试策略模式,真香!!3.操!Java中xx≠null的新语法是什么?4、SpringBoot2.5发布,深色模式太炸了!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!
