自定义解码器
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);@Overridepublic 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);}@Overrideprotected Supplier getData(FeignResponse obj) {return obj::getData;}@Overrideprotected Supplier<String> getCode(FeignResponse obj) {return obj::getCode;}@Overrideprotected Supplier<String> getMes(FeignResponse obj) {return obj::getMsg;}@Overrideprotected boolean isSuccess(FeignResponse obj) {return obj.isSuccess();}@Overrideprotected 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 {@Beanpublic Decoder decoder(ObjectMapper objectMapper) {// 方法入口:feign.InvocationContext , 默认执行链: ResponseEntityDecoder、OptionalDecoder、SpringDecoderreturn 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{@Overridepublic List<User> findWrapUsers(User user) {return null;}@Overridepublic User updateWrapperUser(User user) {return null;}@Overridepublic FeignResponse<List<User>> findWrapUsers2(User user) {return null;}@Overridepublic FeignResponse<User> updateWrapperUser2(User user) {return null;}@Overridepublic FeignResponse<User> handlerError(User user) {return null;}
}