可能有这么一个场景,项目需要接入某个平台,需要使用http调平台的各API,假设API需要通用基本参数,比如时间戳,Token,AppKey…参数,我们就可以把这些参数封装成一个类(通用参数基本类),然后在把各个接口具体的参数同样的封装成一个类,去继承这个通用基本参数类。
如果因为业务的不同,某接口请求封装的类可能存在不需要拼接的字段,这时候就可以自定义注解,反射的时候判断字段是否注解,做出不同的判断
@Slf4jpublic class ShpFactory {public static final String GET_ITEM_BASE_INFO = "/api/v2/product/get_item_base_info";public static final String GET_ITEM_LIST = "/api/v2/product/get_item_list";public static final String GET_MODEL_LIST = "/api/v2/product/get_model_list";public static final String AMPERSAND = "&";public static final String POST = "POST";public static final String GET = "GET";public static ShpBasePlatformRqDTO getRequestClient(String url, ShpBaseConfig config) {ShpBasePlatformRqDTO request = null;try {switch (url) {case GET_ITEM_BASE_INFO:request = ShpPlatformGetItemInfoListRqDTO.create(config);break;case GET_ITEM_LIST:request = ShpPlatformGetItemListRqDTO.create(config);break;case GET_MODEL_LIST:request = ShpPlatformGetModelListRqDTO.create(config);default:new IllegalArgumentException("参数错误,没有对应的实现类");}} catch (Exception e) {log.info("创建请求对象失败了,失败原因是:{},堆栈信息是:{}", JSON.toJSONString(e.getMessage()),JSON.toJSONString(e.getStackTrace()));}return request;}public static long getTimestamp() {return System.currentTimeMillis() / 1000;}/*** 拼接真实的url,get请求的对象需要** @param request* @return* @throws NoSuchMethodException* @throws InvocationTargetException* @throws IllegalAccessException*/public static String getUrl(String uri, ShpBasePlatformRqDTO request, ShpBaseConfig config) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {String url = String.format(config.getHost() + uri + "?partner_id=%s&shop_id=%s×tamp=%s&access_token=%s&sign=%s",config.getAppKey(), config.getShopId(),request.getTimestamp(), request.getAccessToken(), request.getSign());if (request.getMethod().equals(GET)) {StringBuilder stringBuilder = new StringBuilder();//遍历request属性,对url进行追加参数List<Field> fields = new ArrayList<Field>(Arrays.asList(request.getClass().getDeclaredFields()));//获取父类属性Class<?> clazz = request.getClass();if (clazz != Object.class) {Class<?> superClazz = clazz.getSuperclass();List<Field> superFields = Arrays.asList(superClazz.getDeclaredFields());List<Field> superFieldList = new ArrayList<Field>(superFields);fields.addAll(superFieldList);}for (Field field : fields) {ShpParam fieldAnnotation = field.getAnnotation(ShpParam.class);if (Objects.nonNull(fieldAnnotation)) {String propertyName = fieldAnnotation.propertyName();Method m = (Method) request.getClass().getMethod("get" + getMethodName(field.getName()));// 调用getter方法获取属性值Object val = m.invoke(request);if (Objects.nonNull(val)) {stringBuilder.append(propertyName);stringBuilder.append("=");if (val instanceof List) {String str4 = StringUtils.join((List) val, ",");stringBuilder.append(str4);} else {stringBuilder.append(val);}stringBuilder.append(AMPERSAND);}}}if (StringUtils.isNotBlank(stringBuilder.toString())) {return url + "&" + urlSplit(stringBuilder.toString());}}return url;}/*** 把一个字符串的第一个字母大写、效率是最高的、** @param fieldName* @return*/protected static String getMethodName(String fieldName) {byte[] items = fieldName.getBytes();items[0] = (byte) ((char) items[0] - 'a' + 'A');return new String(items);}/*** 字符串中如果最后一位是"&",则截取掉最后一位** @param wholeUrl* @return*/protected static String urlSplit(String wholeUrl) {if (AMPERSAND.equals(wholeUrl.substring(wholeUrl.length() - 1))) {return wholeUrl.substring(0, wholeUrl.length() - 1);}return wholeUrl;}public static String buildSign(String url, ShpBaseConfig config) {String baseSign= String.format("%s%s%s%s%s", config.getAppKey(), url, getTimestamp(), config.getAccessToken(), config.getShopId());return HashingUtils.hmacSHA256Digest(baseSign, config.getAppSecret());}}
案列
@Componentpublic class ApplicationContextHelper implements ApplicationContextAware {private static ApplicationContext applicationContext;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {ApplicationContextHelper.applicationContext = applicationContext;}public static ApplicationContext getApplicationContext(){return ApplicationContextHelper.applicationContext;}public static<T> T getBean(Class<T> targetClz){T beanInstance = null;//优先按type查try {beanInstance = (T) applicationContext.getBean(targetClz);}catch (Exception e){}//按name查if(beanInstance == null){String simpleName = targetClz.getSimpleName();//首字母小写simpleName = Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);beanInstance = (T) applicationContext.getBean(simpleName);}return beanInstance;}public static Object getBean(String claz){return ApplicationContextHelper.applicationContext.getBean(claz);}}
调用API的url,appkey.. 单独写一个配置类
@Component@Datapublic class AliBaseConfog {@Value("${ali.category.appKey}")private String appKey;@Value("${ali.category.accessToken}")private String accessToken;@Value("${ali.category.url}")private String url;}
声明配置DTO类
@Data@Component@Scope(value = "prototype")public class AliBaseConfigDTO<T> {public String host;public Integer appKey;private String accessToken;private T data;}
自定义,用反射来判断类属性是否拼Get请求
/*** Ali url后面是否追加属性参数** @author liuhuan*/@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface AliParam {/*** 属性名 所有Ali get请求需要拼接参数都要打上这个注解** @return*/String propertyName() default "";}
接入某平台API,可能需要一些通用参数,但是具体的接口又需要不同的参数,所有分别把参数封装
@Datapublic class AliPlatformRqDTO implements Serializable {@AliParam(propertyName="_aop_timestamp")private String aopTimestamp;@AliParam(propertyName="_aop_signature")private String aopSignature;@AliParam(propertyName="access_token")private String accessToken;private String url;private String method;}
具体接口需要的参数也封装,继承平台通用参数类
@EqualsAndHashCode(callSuper = true)@Datapublic class AliCategoryRqDTO extends AliBaseCategoryRqDTO {@AliParam(propertyName = "categoryID")private Long categoryID;public static AliBaseCategoryRqDTO create(AliBaseConfigDTO config) {return null;}}
平台通用返回值也封装
@Data@AllArgsConstructor@NoArgsConstructorpublic class AliPlatformRpDTO {private Boolean succes;private String errorMsg;private String errorCode;}
具体接口返回对象封装
@Data@AllArgsConstructor@NoArgsConstructorpublic class AliCategoryGetRpDTO extends AliPlatformRpDTO {private List<CategoryInfo> categoryInfo;}
