阿里P8架构师教你kill祖传石山代码重复/大量ifelse( 三 )


通过注解实现了对API参数的描述 。 看反射如何配合注解实现动态的接口参数组装:
【阿里P8架构师教你kill祖传石山代码重复/大量ifelse】private static String remoteCall(AbstractAPI api) throws IOException {// 从类上获得BankAPI注解 , 然后拿到其URL属性 , 后续进行远程调用BankAPI bankAPI = api.getClass().getAnnotation(BankAPI.class);bankAPI.url();StringBuilder stringBuilder = new StringBuilder();// 使用stream快速实现获取类中所有带BankAPIField注解的字段 , 并把字段按order属性排序 , 然后设置私有字段反射可访问 。Arrays.stream(api.getClass().getDeclaredFields()) //获得所有字段//查找标记了注解的字段.filter(field -> field.isAnnotationPresent(BankAPIField.class))// 根据注解中的order对字段排序.sorted(Comparator.comparingInt(a -> a.getAnnotation(BankAPIField.class).order())).peek(field -> field.setAccessible(true)) //设置可以访问私有字段.forEach(field -> {// 实现了反射获取注解的值 , 然后根据BankAPIField拿到的参数类型 , 按照三种标准进行格式化 , 将所有参数的格式化逻辑集中在了这一处// 获得注解BankAPIField bankAPIField = field.getAnnotation(BankAPIField.class);Object valuehttp://kandian.youth.cn/index/= "";try {// 反射获取字段值value = http://kandian.youth.cn/index/field.get(api);} catch (IllegalAccessException e) {e.printStackTrace();}// 根据字段类型以正确的填充方式格式化字符串switch (bankAPIField.type()) {case"S": {stringBuilder.append(String.format("%-" + bankAPIField.length() + "s", value.toString()).replace(' ', '_'));break;}case "N": {stringBuilder.append(String.format("%" + bankAPIField.length() + "s", value.toString()).replace(' ', '0'));break;}case "M": {if (!(value instanceof BigDecimal))throw new RuntimeException(String.format("{} 的 {} 必须是BigDecimal", api, field));stringBuilder.append(String.format("%0" + bankAPIField.length() + "d", ((BigDecimal) value).setScale(2, RoundingMode.DOWN).multiply(new BigDecimal("100")).longValue()));break;}default:break;}});// 实现参数加签和请求调用// 签名逻辑stringBuilder.append(DigestUtils.md2Hex(stringBuilder.toString()));String param = stringBuilder.toString();long begin = System.currentTimeMillis();//发请求String result = 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);return result;}所有处理参数排序、填充、加签、请求调用的核心逻辑 , 都汇聚在remoteCall 。 有这方法 , BankService中每一个接口的实现就非常简单了 , 只是参数的组装 , 然后调用remoteCall 。
阿里P8架构师教你kill祖传石山代码重复/大量ifelse文章插图
涉及类结构性的通用处理 , 都可按照该模式减少重复代码 。

  • 反射使得我们在不知类结构时 , 按固定逻辑处理类成员
  • 注解给我们为这些成员补充元数据的能力 , 使得我们利用反射实现通用逻辑的时候 , 可以从外部获得更多我们关心的数据
4 属性拷贝对于三层架构系统 , 层间解耦及每层对数据的不同需求 , 每层都会有自己的POJO实体 。手动写这些实体之间的赋值代码 , 容易出错 。 对于复杂业务系统 , 实体有几十甚至几百个属性也很正常 。 比如ComplicatedOrderDTO , 描述一个订单中几十个属性 。 如果转换为一个类似的DO , 复制其中大部分的字段 , 然后把数据入库 , 势必需要进行很多属性映射赋值操作 。 就像这样 , 密密麻麻的代码是不是已经让你头晕了?
ComplicatedOrderDTO orderDTO = new ComplicatedOrderDTO();ComplicatedOrderDO orderDO = new ComplicatedOrderDO();orderDO.setAcceptDate(orderDTO.getAcceptDate());orderDO.setAddress(orderDTO.getAddress());orderDO.setAddressId(orderDTO.getAddressId());orderDO.setCancelable(orderDTO.isCancelable());orderDO.setCommentable(orderDTO.isComplainable()); //属性错误orderDO.setComplainable(orderDTO.isCommentable()); //属性错误orderDO.setCancelable(orderDTO.isCancelable());orderDO.setCouponAmount(orderDTO.getCouponAmount());orderDO.setCouponId(orderDTO.getCouponId());orderDO.setCreateDate(orderDTO.getCreateDate());orderDO.setDirectCancelable(orderDTO.isDirectCancelable());orderDO.setDeliverDate(orderDTO.getDeliverDate());orderDO.setDeliverGroup(orderDTO.getDeliverGroup());orderDO.setDeliverGroupOrderStatus(orderDTO.getDeliverGroupOrderStatus());orderDO.setDeliverMethod(orderDTO.getDeliverMethod());orderDO.setDeliverPrice(orderDTO.getDeliverPrice());orderDO.setDeliveryManId(orderDTO.getDeliveryManId());orderDO.setDeliveryManMobile(orderDO.getDeliveryManMobile()); //对象错误orderDO.setDeliveryManName(orderDTO.getDeliveryManName());orderDO.setDistance(orderDTO.getDistance());orderDO.setExpectDate(orderDTO.getExpectDate());orderDO.setFirstDeal(orderDTO.isFirstDeal());orderDO.setHasPaid(orderDTO.isHasPaid());orderDO.setHeadPic(orderDTO.getHeadPic());orderDO.setLongitude(orderDTO.getLongitude());orderDO.setLatitude(orderDTO.getLongitude()); //属性赋值错误orderDO.setMerchantAddress(orderDTO.getMerchantAddress());orderDO.setMerchantHeadPic(orderDTO.getMerchantHeadPic());orderDO.setMerchantId(orderDTO.getMerchantId());orderDO.setMerchantAddress(orderDTO.getMerchantAddress());orderDO.setMerchantName(orderDTO.getMerchantName());orderDO.setMerchantPhone(orderDTO.getMerchantPhone());orderDO.setOrderNo(orderDTO.getOrderNo());orderDO.setOutDate(orderDTO.getOutDate());orderDO.setPayable(orderDTO.isPayable());orderDO.setPaymentAmount(orderDTO.getPaymentAmount());orderDO.setPaymentDate(orderDTO.getPaymentDate());orderDO.setPaymentMethod(orderDTO.getPaymentMethod());orderDO.setPaymentTimeLimit(orderDTO.getPaymentTimeLimit());orderDO.setPhone(orderDTO.getPhone());orderDO.setRefundable(orderDTO.isRefundable());orderDO.setRemark(orderDTO.getRemark());orderDO.setStatus(orderDTO.getStatus());orderDO.setTotalQuantity(orderDTO.getTotalQuantity());orderDO.setUpdateTime(orderDTO.getUpdateTime());orderDO.setName(orderDTO.getName());orderDO.setUid(orderDTO.getUid());