自定义解码器
package com.itheima.feign;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.exception.BusinessException;
import feign.Response;
import feign.Util;
import feign.codec.Decoder;
import org.springframework.core.ResolvableType;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
/**
* @author 阿呆
* @desciption:
* @since 2023/4/23 23:09
*/
public abstract class AbstractCustomDecoder <T> implements Decoder{
protected final Class<T> targetClazz;
protected Set<Class> clazzSet;
protected ObjectMapper objectMapper;
protected AbstractCustomDecoder(ObjectMapper objectMapper) {
this.targetClazz = retrieveAnnotationType();
this.objectMapper = objectMapper;
this.clazzSet = new HashSet<>();
this.clazzSet.add(this.targetClazz);
this.fillTargetClazz(this.clazzSet);
}
protected abstract <D> Supplier<D> getData(T obj);
protected abstract <C> Supplier<C> getCode(T obj);
protected abstract <M> Supplier<M> getMes(T obj);
protected abstract boolean isSuccess(T obj);
protected abstract void fillTargetClazz(Set<Class> clazzSet);
@Override
public Object decode(Response response, Type type) throws IOException {
return customDecoder(response, type);
}
/**
* 检索泛型对应的实际类型
* @return
*/
private Class<T> retrieveAnnotationType(){
Type type = getClass().getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
for (Type actualTypeArgument : parameterizedType.getActualTypeArguments()) {
if (actualTypeArgument instanceof Class) {
return (Class) actualTypeArgument;
}
}
}
return null;
}
protected Object customDecoder(Response response, Type type) throws IOException {
String body = Util.toString(response.body().asReader(StandardCharsets.UTF_8));
Class<?> clazz = ResolvableType.forType(type).getRawClass();
if (this.clazzSet.contains(clazz)){
return this.objectMapper.readValue(body,clazz);
}
try{
return this.objectMapper.readValue(body,clazz);
}catch (Exception e){
JavaType paramterType = this.objectMapper.getTypeFactory().constructType(type);
JavaType resultType = this.objectMapper.getTypeFactory().constructParametricType(this.targetClazz, paramterType);
T data = (T)this.objectMapper.readValue(body,resultType);
if (isSuccess(data)){
return getData(data).get();
}
throw new BusinessException(Integer.valueOf(getCode(data).get().toString()),getMes(data).get().toString());
}
}
}
package com.itheima.feign;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.common.entry.FeignResponse;
import com.itheima.common.entry.Result;
import feign.codec.Decoder;
import feign.optionals.OptionalDecoder;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Set;
import java.util.function.Supplier;
/**
* @author 阿呆
* @desciption: @Configuration 加入 该注解后 全局有效 , 替换 SpringDecoder 没有 局部引用也会生效
* @since 2023/4/23 23:27
*/
public class CustomDecoderConfig extends AbstractCustomDecoder<FeignResponse> {
protected CustomDecoderConfig(ObjectMapper objectMapper) {
super(objectMapper);
}
@Override
protected Supplier getData(FeignResponse obj) {
return obj::getData;
}
@Override
protected Supplier<String> getCode(FeignResponse obj) {
return obj::getCode;
}
@Override
protected Supplier<String> getMes(FeignResponse obj) {
return obj::getMsg;
}
@Override
protected boolean isSuccess(FeignResponse obj) {
return obj.isSuccess();
}
@Override
protected void fillTargetClazz(Set<Class> clazzSet) {
clazzSet.add(Result.class);
}
}
package com.itheima.feign;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.common.entry.FeignResponse;
import com.itheima.common.entry.Result;
import feign.codec.Decoder;
import feign.optionals.OptionalDecoder;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Set;
import java.util.function.Supplier;
/**
* @author 阿呆
* @desciption: @Configuration 加入 该注解后 全局有效 , 替换 SpringDecoder 没有 局部引用也会生效
* @since 2023/4/23 23:27
*/
@Configuration
public class FeignClientCustomDecoderConfig {
@Bean
public Decoder decoder(ObjectMapper objectMapper) {
// 方法入口:feign.InvocationContext , 默认执行链: ResponseEntityDecoder、OptionalDecoder、SpringDecoder
return new ResponseEntityDecoder(
new OptionalDecoder(
new CustomDecoderConfig(objectMapper)
)
);
}
}
局部、全价有效
全价有效,FeignClientCustomDecoderConfig需要配置@Configuration 进行标记,局部有效则去掉。没有配置的默认使用SpringDecoder 进行解码
package com.itheima.client;
import com.itheima.common.entry.FeignResponse;
import com.itheima.common.entry.User;
import com.itheima.feign.FeignClientCustomDecoderConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
/**
* 局部有效,FeignClientCustomDecoderConfig 不要注入bean , 不然会替换掉默认的SpringDecode 解码器
* value 服务名称 contextId bean名称
*/
@FeignClient(value = "nacos-client-feign", contextId = "nacosClientFeignWrapper", configuration = FeignClientCustomDecoderConfig.class)
public interface NacosClientFeignWrapper {
@RequestMapping("/findWrapUsers")
List<User> findWrapUsers(@RequestBody User user);
@RequestMapping("/updateWrapperUser")
User updateWrapperUser(@RequestBody User user);
@RequestMapping("/findWrapUsers")
FeignResponse<List<User>> findWrapUsers2(@RequestBody User user);
@RequestMapping("/updateWrapperUser")
FeignResponse<User> updateWrapperUser2(@RequestBody User user);
@RequestMapping("/handlerError")
FeignResponse<User> handlerError(@RequestBody User user);
}
Feign接口编写,继承实现方式
package com.itheima.client;
import com.itheima.common.entry.FeignResponse;
import com.itheima.common.entry.User;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
# 共有类
/**
* @author 阿呆
* @desciption:
* @since 2024/10/26 20:26
*/
public interface UserWebApi {
@RequestMapping("/user/findWrapUsers")
List<User> findWrapUsers(@RequestBody User user);
@RequestMapping("/user/updateWrapperUser")
User updateWrapperUser(@RequestBody User user);
@RequestMapping("/user/findWrapUsers")
FeignResponse<List<User>> findWrapUsers2(@RequestBody User user);
@RequestMapping("/user/updateWrapperUser")
FeignResponse<User> updateWrapperUser2(@RequestBody User user);
@RequestMapping("/user/handlerError")
FeignResponse<User> handlerError(@RequestBody User user);
}
# 调用方 实现接口
package com.itheima.client;
import com.itheima.feign.FeignClientCustomDecoderConfig;
import org.springframework.cloud.openfeign.FeignClient;
/**
* @author 阿呆
* @desciption:
* @since 2024/10/26 20:27
*/
@FeignClient(value = "nacos-client-feign", contextId = "nacosClientFeignWrapper", configuration = FeignClientCustomDecoderConfig.class)
public interface UserFeignApi extends UserWebApi{
}
# 提供方服务器接口实现
package com.itheima.client;
import com.itheima.common.entry.FeignResponse;
import com.itheima.common.entry.User;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author 阿呆
* @desciption:
* @since 2024/10/26 20:28
*/
@RestController
public class UserController implements UserWebApi{
@Override
public List<User> findWrapUsers(User user) {
return null;
}
@Override
public User updateWrapperUser(User user) {
return null;
}
@Override
public FeignResponse<List<User>> findWrapUsers2(User user) {
return null;
}
@Override
public FeignResponse<User> updateWrapperUser2(User user) {
return null;
}
@Override
public FeignResponse<User> handlerError(User user) {
return null;
}
}