问题
一般在对接客户接口时,都会使用多个策略类处理,并且不同的客户返回值不同,因此比较好的方案是使用泛型来处理。
然而现实是,feign或openfeign会擦除泛型,将返回的对象转为LinkedHashMap,导致转json反序列化成对象时异常。
思路
debug可以发现,在decode时,response返回的是所需要数据,问题在于反序列化,所以要在decode时,针对反序列化做一些处理。
在参考一些资料和文章后,最终最适合我的方案是在请求头加泛型类的全限定名,decode时使用json转换成所需类。
流程如下:
- 发送请求在header加入泛型类名
- 自定义decode取出header,反序列化为所需类
- 返回结果
准备
服务端需要统一返回值,比如我自定义的统一返回值类型为R
主要代码
Decoder
import com.fasterxml.jackson.databind.JsonNode;
import feign.FeignException;
import feign.Response;
import feign.codec.Decoder;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class FeignGenericDecoder implements Decoder {
public static final String GENERICS_HEADER = "generics-header";
private final Decoder decoder;
public FeignGenericDecoder(@Lazy Decoder decoder) {
this.decoder = decoder;
}
@SuppressWarnings({"unchecked", "rawtypes"})
@SneakyThrows
@Override
public Object decode(Response response, Type type) throws FeignException {
// Annotation[] annotations = response.request().requestTemplate().methodMetadata().method().getAnnotations();
Object returnObject;
if (isParameterizeHttpEntity(type)) {
type = ((ParameterizedType) type).getActualTypeArguments()[0];
Object decodedObject = decoder.decode(response, type);
returnObject = createResponse(decodedObject, response);
} else if (isHttpEntity(type)) {
returnObject = createResponse(null, response);
} else {
returnObject = decoder.decode(response, type);
}
// 以上是原默认实现,复制过来,为了拿到returnObject
if (returnObject instanceof R) {
Map<String, Collection<String>> map = response.request().headers();
Collection<String> generics = map.get(GENERICS_HEADER);
if (CollectionUtils.isEmpty(generics)) {
return returnObject;
}
// 返回值转json
String body = JacksonUtils.toJson(returnObject);
JsonNode jsonNode = JacksonUtils.getMapper().readTree(body);
if (jsonNode.get("code").intValue() != 200) {
return returnObject;
}
Class<?> clazz = Class.forName(generics.iterator().next());
// 拿出result,实例化对象(genericsHeader 指定的类型),然后重新set
JsonNode data = jsonNode.get("data");
if (data.isArray()) {
List<?> list = JacksonUtils.toList(data.toString(), clazz);
((R) returnObject).setData(list);
} else if (data.isObject()) {
Object obj = JacksonUtils.toObj(data.toString(), clazz);
((R) returnObject).setData(obj);
}
}
return returnObject;
}
private boolean isParameterizeHttpEntity(Type type) {
if (type instanceof ParameterizedType) {
return isHttpEntity(((ParameterizedType) type).getRawType());
}
return false;
}
private boolean isHttpEntity(Type type) {
if (type instanceof Class) {
Class<?> c = (Class<?>) type;
return HttpEntity.class.isAssignableFrom(c);
}
return false;
}
@SuppressWarnings({"unchecked"})
private <T> ResponseEntity<T> createResponse(Object instance, Response response) {
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
for (String key : response.headers().keySet()) {
headers.put(key, new LinkedList<>(response.headers().get(key)));
}
return new ResponseEntity<>((T) instance, headers, HttpStatus.valueOf(response.status()));
}
}
使用(代码片段)
@FeignClient(value = "服务端", contextId = "OaMallFeign", fallbackFactory = OaMallFeignFallback.class, configuration = FeignGenericDecoder.class)
public interface OaMallFeign {
@PostMapping(value = "/feign/oa/querySku")
<T> R<List<T>> querySku(@RequestBody @Validated SkuQueryDTO params, @RequestHeader(FeignGenericDecoder.GENERICS_HEADER) String generic);
}
@Component
public class OaMallFeignFallback implements FallbackFactory<OaMallFeign> {
@Override
public OaMallFeign create(Throwable cause) {
log.error("OaMallFeignFallback接口调用失败", cause);
return new OaMallFeign() {
@Override
public <T> R<List<T>> querySku(SkuQueryDTO params, String generic) {
return null;
}
}
}
}
@Service
public class OaMallFeignService {
@Autowired
private OaMallFeign oaMallFeign;
public <T> List<T> querySku(SkuQueryDTO params, Class<T> clz) {
return R.get(oaMallFeign.querySku(params, clz.getName()));
}
}
客户端调用
@Autowired
private OaMallFeignService oaMallFeignService;
public List<SkuVO> querySku() {
SkuQueryDTO params = new SkuQueryDTO().setSkuIds(Collections.singletonList(1L)).setCompanyId(1L);
List<SkuVO> list = oaMallFeignService.querySku(params, SkuVO.class);
log.info("list --->>> {}", JacksonUtils.toJson(list));
return list;
}