一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第22天,点击查看活动详情。
如何统一处理业务响应操作失败的情况?来解决 大量调用者调用时进行业务是否成功的判断.
使用Feign中 Decoder功能
Decoder为解码器,用于将响应信息给反序列化为Java对象.使用什么样的序列化方式就是用对应的解码器. 解码器Feign内部实现使用装饰器+委派模式.
解码器分为全局和部分,配置方式与Request Interceptor配置类似.以下仅介绍配置文件中的配置.
public class DefaultDecoder implements Decoder {
Decoder delegate; // 委派对象
public DefaultDecoder() {
// 容器中获取解码器, 需要依赖一个配置类才能正确的获取到解码器
// this.delegate = ApplicationContextUtils.getApplicationContext().getBean(Decoder.class);
}
@Override
public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
if (delegate == null) {
// 不放在构造函数中,是因为在解析器创建时,spring 容器还未创建出来
this.delegate = ApplicationContextUtils.getApplicationContext().getBean(Decoder.class);
}
Object o = delegate.decode(response, type);
DefaultResp resp = (DefaultResp)o;
if (resp.getCode() != null && resp.getCode() == 0) {
// 操作成功
return o;
}
// 操作失败,统一抛出异常
throw new FeignClientException(resp.getCode(), resp.getMsg());
}
}
feign:
client:
config:
school:
decoder: com.test.growth.school.feign.DefaultDecoder #解码器
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import feign.codec.Decoder;
import feign.optionals.OptionalDecoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* TODO
*
* Decoder decoder = null;
* try {
* decoder = ApplicationContextUtils.getApplicationContext().getBean(Decoder.class);
* } catch (NoSuchBeanDefinitionException e) {
* FeignClientsConfiguration configuration = ApplicationContextUtils.getApplicationContext().getBean(FeignClientsConfiguration.class);
* decoder = configuration.feignDecoder();
*/
@Configuration
public class CustomFeignClientsConfiguration extends FeignClientsConfiguration {
@Resource
private Jackson2ObjectMapperBuilder builder;
/**
* 默认解析器,
* 将响应结果,下划线转驼峰
*
*/
@Bean
public Decoder customFeignDecoder() {
return new OptionalDecoder(
new ResponseEntityDecoder(new SpringDecoder(this ::jacksonHttpMessageConverters)));
}
public HttpMessageConverters jacksonHttpMessageConverters() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = builder.build();
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
converter.setObjectMapper(objectMapper);
//设置中文编码格式
List<MediaType> list = new ArrayList<>();
list.add(MediaType.APPLICATION_JSON);
converter.setSupportedMediaTypes(list);
return new HttpMessageConverters(converter);
}
}
基于解码器进行二次封装
抽象父类BusinessErrorDecoder,泛型为接口响应的统一数据类型,统一处理在业务响应失败的情况下抛出异常,新增一种数据类型,只需要2步: 1. 新建自己的ErrorDecoder继承BusinessErrorDecoder,实现其未实现的方法, 2. 配置解析器给哪个Client使用
import com.dahai.video.ApplicationContextUtils;
import com..growth.school.feign.FeignClientException;
import feign.FeignException;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import java.io.IOException;
import java.lang.reflect.Type;
/**
* 业务错误-解析器
*/
public abstract class BusinessErrorDecoder<T> implements Decoder {
protected Decoder delegate;
public BusinessErrorDecoder() {
}
@Override
public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
if (delegate == null) {
// 不放在构造函数中,是因为在解析器创建时,spring 容器还未创建出来
this.delegate = ApplicationContextUtils.getApplicationContext().getBean(Decoder.class);
}
Object o = delegate.decode(response, type);
T t = (T) o;
boolean flag = this.isBusinessError(t);
if (flag) {
throw new FeignClientException(this.getCode(t), this.getMsg(t));
}
return o;
}
/**
* 判断业务码是否成功
*/
protected abstract boolean isBusinessError(T obj);
/**
* 获取业务码
*/
protected abstract Integer getCode(T obj);
/**
* 获取错误信息
*/
protected abstract String getMsg(T obj);
}
import com.test.growth.school.feign.resp.DefaultResp;
/**
* 解析
*/
public class DefaultDecoder extends BusinessErrorDecoder<DefaultResp> {
@Override
protected boolean isBusinessError(DefaultResp obj) {
return obj.getCode() != null && obj.getCode() != 0;
}
@Override
protected Integer getCode(DefaultResp obj) {
return obj.getCode();
}
@Override
protected String getMsg(DefaultResp obj) {
return obj.getMsg();
}
}
import com.dahai.video.common.exception.BusinessException;
/**
* feign 客户端异常
*/
public class FeignClientException extends BusinessException {
public FeignClientException(Integer code, String message) {
super(code, message);
}
}