可能有这么一个场景,项目需要接入某个平台,需要使用http调平台的各API,假设API需要通用基本参数,比如时间戳,Token,AppKey…参数,我们就可以把这些参数封装成一个类(通用参数基本类),然后在把各个接口具体的参数同样的封装成一个类,去继承这个通用基本参数类。
如果因为业务的不同,某接口请求封装的类可能存在不需要拼接的字段,这时候就可以自定义注解,反射的时候判断字段是否注解,做出不同的判断
@Slf4j
public 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());
}
}
案列
@Component
public class ApplicationContextHelper implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public 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
@Data
public 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,可能需要一些通用参数,但是具体的接口又需要不同的参数,所有分别把参数封装
@Data
public 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)
@Data
public class AliCategoryRqDTO extends AliBaseCategoryRqDTO {
@AliParam(propertyName = "categoryID")
private Long categoryID;
public static AliBaseCategoryRqDTO create(AliBaseConfigDTO config) {
return null;
}
}
平台通用返回值也封装
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AliPlatformRpDTO {
private Boolean succes;
private String errorMsg;
private String errorCode;
}
具体接口返回对象封装
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AliCategoryGetRpDTO extends AliPlatformRpDTO {
private List<CategoryInfo> categoryInfo;
}