可能有这么一个场景,项目需要接入某个平台,需要使用http调平台的各API,假设API需要通用基本参数,比如时间戳,Token,AppKey…参数,我们就可以把这些参数封装成一个类(通用参数基本类),然后在把各个接口具体的参数同样的封装成一个类,去继承这个通用基本参数类。
如果因为业务的不同,某接口请求封装的类可能存在不需要拼接的字段,这时候就可以自定义注解,反射的时候判断字段是否注解,做出不同的判断

  1. @Slf4j
  2. public class ShpFactory {
  3. public static final String GET_ITEM_BASE_INFO = "/api/v2/product/get_item_base_info";
  4. public static final String GET_ITEM_LIST = "/api/v2/product/get_item_list";
  5. public static final String GET_MODEL_LIST = "/api/v2/product/get_model_list";
  6. public static final String AMPERSAND = "&";
  7. public static final String POST = "POST";
  8. public static final String GET = "GET";
  9. public static ShpBasePlatformRqDTO getRequestClient(String url, ShpBaseConfig config) {
  10. ShpBasePlatformRqDTO request = null;
  11. try {
  12. switch (url) {
  13. case GET_ITEM_BASE_INFO:
  14. request = ShpPlatformGetItemInfoListRqDTO.create(config);
  15. break;
  16. case GET_ITEM_LIST:
  17. request = ShpPlatformGetItemListRqDTO.create(config);
  18. break;
  19. case GET_MODEL_LIST:
  20. request = ShpPlatformGetModelListRqDTO.create(config);
  21. default:
  22. new IllegalArgumentException("参数错误,没有对应的实现类");
  23. }
  24. } catch (Exception e) {
  25. log.info("创建请求对象失败了,失败原因是:{},堆栈信息是:{}", JSON.toJSONString(e.getMessage()),JSON.toJSONString(e.getStackTrace()));
  26. }
  27. return request;
  28. }
  29. public static long getTimestamp() {
  30. return System.currentTimeMillis() / 1000;
  31. }
  32. /**
  33. * 拼接真实的url,get请求的对象需要
  34. *
  35. * @param request
  36. * @return
  37. * @throws NoSuchMethodException
  38. * @throws InvocationTargetException
  39. * @throws IllegalAccessException
  40. */
  41. public static String getUrl(String uri, ShpBasePlatformRqDTO request, ShpBaseConfig config) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
  42. String url = String.format(config.getHost() + uri + "?partner_id=%s&shop_id=%s&timestamp=%s&access_token=%s&sign=%s",
  43. config.getAppKey(), config.getShopId(),request.getTimestamp(), request.getAccessToken(), request.getSign());
  44. if (request.getMethod().equals(GET)) {
  45. StringBuilder stringBuilder = new StringBuilder();
  46. //遍历request属性,对url进行追加参数
  47. List<Field> fields = new ArrayList<Field>(Arrays.asList(request.getClass().getDeclaredFields()));
  48. //获取父类属性
  49. Class<?> clazz = request.getClass();
  50. if (clazz != Object.class) {
  51. Class<?> superClazz = clazz.getSuperclass();
  52. List<Field> superFields = Arrays.asList(superClazz.getDeclaredFields());
  53. List<Field> superFieldList = new ArrayList<Field>(superFields);
  54. fields.addAll(superFieldList);
  55. }
  56. for (Field field : fields) {
  57. ShpParam fieldAnnotation = field.getAnnotation(ShpParam.class);
  58. if (Objects.nonNull(fieldAnnotation)) {
  59. String propertyName = fieldAnnotation.propertyName();
  60. Method m = (Method) request.getClass().getMethod(
  61. "get" + getMethodName(field.getName()));
  62. // 调用getter方法获取属性值
  63. Object val = m.invoke(request);
  64. if (Objects.nonNull(val)) {
  65. stringBuilder.append(propertyName);
  66. stringBuilder.append("=");
  67. if (val instanceof List) {
  68. String str4 = StringUtils.join((List) val, ",");
  69. stringBuilder.append(str4);
  70. } else {
  71. stringBuilder.append(val);
  72. }
  73. stringBuilder.append(AMPERSAND);
  74. }
  75. }
  76. }
  77. if (StringUtils.isNotBlank(stringBuilder.toString())) {
  78. return url + "&" + urlSplit(stringBuilder.toString());
  79. }
  80. }
  81. return url;
  82. }
  83. /**
  84. * 把一个字符串的第一个字母大写、效率是最高的、
  85. *
  86. * @param fieldName
  87. * @return
  88. */
  89. protected static String getMethodName(String fieldName) {
  90. byte[] items = fieldName.getBytes();
  91. items[0] = (byte) ((char) items[0] - 'a' + 'A');
  92. return new String(items);
  93. }
  94. /**
  95. * 字符串中如果最后一位是"&",则截取掉最后一位
  96. *
  97. * @param wholeUrl
  98. * @return
  99. */
  100. protected static String urlSplit(String wholeUrl) {
  101. if (AMPERSAND.equals(wholeUrl.substring(wholeUrl.length() - 1))) {
  102. return wholeUrl.substring(0, wholeUrl.length() - 1);
  103. }
  104. return wholeUrl;
  105. }
  106. public static String buildSign(String url, ShpBaseConfig config) {
  107. String baseSign= String.format("%s%s%s%s%s", config.getAppKey(), url, getTimestamp(), config.getAccessToken(), config.getShopId());
  108. return HashingUtils.hmacSHA256Digest(baseSign, config.getAppSecret());
  109. }
  110. }

案列

  1. @Component
  2. public class ApplicationContextHelper implements ApplicationContextAware {
  3. private static ApplicationContext applicationContext;
  4. @Override
  5. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  6. ApplicationContextHelper.applicationContext = applicationContext;
  7. }
  8. public static ApplicationContext getApplicationContext(){
  9. return ApplicationContextHelper.applicationContext;
  10. }
  11. public static<T> T getBean(Class<T> targetClz){
  12. T beanInstance = null;
  13. //优先按type查
  14. try {
  15. beanInstance = (T) applicationContext.getBean(targetClz);
  16. }catch (Exception e){
  17. }
  18. //按name查
  19. if(beanInstance == null){
  20. String simpleName = targetClz.getSimpleName();
  21. //首字母小写
  22. simpleName = Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
  23. beanInstance = (T) applicationContext.getBean(simpleName);
  24. }
  25. return beanInstance;
  26. }
  27. public static Object getBean(String claz){
  28. return ApplicationContextHelper.applicationContext.getBean(claz);
  29. }
  30. }

调用API的url,appkey.. 单独写一个配置类

  1. @Component
  2. @Data
  3. public class AliBaseConfog {
  4. @Value("${ali.category.appKey}")
  5. private String appKey;
  6. @Value("${ali.category.accessToken}")
  7. private String accessToken;
  8. @Value("${ali.category.url}")
  9. private String url;
  10. }

声明配置DTO类

  1. @Data
  2. @Component
  3. @Scope(value = "prototype")
  4. public class AliBaseConfigDTO<T> {
  5. public String host;
  6. public Integer appKey;
  7. private String accessToken;
  8. private T data;
  9. }

自定义,用反射来判断类属性是否拼Get请求

  1. /**
  2. * Ali url后面是否追加属性参数
  3. *
  4. * @author liuhuan
  5. */
  6. @Target(ElementType.FIELD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface AliParam {
  9. /**
  10. * 属性名 所有Ali get请求需要拼接参数都要打上这个注解
  11. *
  12. * @return
  13. */
  14. String propertyName() default "";
  15. }

接入某平台API,可能需要一些通用参数,但是具体的接口又需要不同的参数,所有分别把参数封装

  1. @Data
  2. public class AliPlatformRqDTO implements Serializable {
  3. @AliParam(propertyName="_aop_timestamp")
  4. private String aopTimestamp;
  5. @AliParam(propertyName="_aop_signature")
  6. private String aopSignature;
  7. @AliParam(propertyName="access_token")
  8. private String accessToken;
  9. private String url;
  10. private String method;
  11. }

具体接口需要的参数也封装,继承平台通用参数类

  1. @EqualsAndHashCode(callSuper = true)
  2. @Data
  3. public class AliCategoryRqDTO extends AliBaseCategoryRqDTO {
  4. @AliParam(propertyName = "categoryID")
  5. private Long categoryID;
  6. public static AliBaseCategoryRqDTO create(AliBaseConfigDTO config) {
  7. return null;
  8. }
  9. }

平台通用返回值也封装

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. public class AliPlatformRpDTO {
  5. private Boolean succes;
  6. private String errorMsg;
  7. private String errorCode;
  8. }

具体接口返回对象封装

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. public class AliCategoryGetRpDTO extends AliPlatformRpDTO {
  5. private List<CategoryInfo> categoryInfo;
  6. }