Feign客户端Fallback全局代理

在网络请求时,可能会出现异常请求,如果还想再异常情况下使系统可用,那么就需要容错处理,使用FeignClient时可对fallback进行配置,但随着接口数不断增加,配置也越来越重复繁琐,且大多容错逻辑均一致,因此需要对容错配置进行代理,提供全局统一容错处理。

前言

Spring Cloud Feign的Fallback实现有两种设置方式:

  • @FeignClient注解fallback属性指定一个实现Feign接口的fallback处理类
  • @FeignClient注解fallbackFactory属性指定一个实现FallbackFactory<T>工厂接口类

由于Fallback大多配合Hystrix实现, 所以需要开启Hystrix:

application.properties
1
feign.hystrix.enabled=true

当然,升级open-feign到4.x就不用这么麻烦了

实现Feign接口的fallback处理类

例如下接口:

1
2
3
4
5
6
7
8
9
10
11
12
@FeignClient(name = "user", fallback = UserFeignFallback.class)
public interface UserFeign {

@PostMapping
void saveUser(User user);

@GetMapping("/{id}")
User getUserByID(@PathVariable("id") String id);

@GetMapping
List<User> listAllUser();
}

所需实现Feign接口的fallback处理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component
public class UserFeignFallback implements UserFeign {

@Override
public void saveUser(User user) {
// do something when saveUser failed.
}

@Override
public User getUserByID(@PathVariable("id") String id) {
// do something when queryUser failed.
return null;
}

@Override
public List<User> listAllUser() {
// do something when listUser failed.
return Collections.emptyList();
}
}

实现FallbackFactory工厂接口类

例如下接口:

1
2
3
4
5
6
7
8
9
10
11
12
@FeignClient(name = "user", fallbackFactory = UserFeignFallbackFactory.class)
public interface UserFeign {

@PostMapping
void saveUser(User user);

@GetMapping("/{id}")
User getUserByID(@PathVariable("id") String id);

@GetMapping
List<User> listAllUser();
}

所需实现实现FallbackFactory工厂接口类:

1
2
3
4
5
6
7
8
9
@Component
public class UserFeignFallbackFactory implements FallbackFactory<UserFeign> {

@Override
public UserFeign create(Throwable cause) {
// log throwable error...
return new UserFeignFallback();
}
}

以上配置在有些时候显得多余且繁琐

代理FeignContext

由于FeignBeanFeignContext(child contexts that allows a set of Specifications to define the beans)构建,所以写一个BeanPostProcessor拦截FeignContext:

1
2
3
4
5
6
7
8
9
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof FeignContext && !(bean instanceof TargetFeignContextWrapper)) {
FeignContext context = (FeignContext) bean;
registerFeignClientFallback(context);
return new TargetFeignContextWrapper(context, beanFactory);
}
return bean;
}

然后在获取FeignTarget时代理Feign客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/** FeignContext wrapper */
static class TargetFeignContextWrapper extends FeignContext {

private final FeignContext delegate;
private final ConfigurableListableBeanFactory beanFactory;
/** HystrixTargeter */
private static Class<?> ClassHystrixTargeter;
/** 默认Target */
private static Class<?> ClassDefaultTargeter;

static {
try {
// package-access class load
ClassHystrixTargeter =
TargetFeignContextWrapper.class.getClassLoader().loadClass("org.springframework.cloud.openfeign.HystrixTargeter");
ClassDefaultTargeter =
TargetFeignContextWrapper.class.getClassLoader().loadClass("org.springframework.cloud.openfeign.DefaultTargeter");
} catch (ClassNotFoundException e) {
log.warn("Unable to load HystrixTargeter.", e);
}
}

public TargetFeignContextWrapper(FeignContext delegate, ConfigurableListableBeanFactory beanFactory) {
this.delegate = delegate;
this.beanFactory = beanFactory;
}

@Override
public <T> T getInstance(String name, Class<T> type) {
// 目标对象
T object = this.delegate.getInstance(name, type);
// 必要进行包装代理
return warpIfNecessary(object);
}

@Override
public <T> Map<String, T> getInstances(String name, Class<T> type) {
Map<String, T> instances = this.delegate.getInstances(name, type);
if (instances == null) {
return null;
}
// 保持原始Bean顺序(链路追踪Sleuth代理的FeignContext返回HashMap,导致bean即使声明了ordered依旧无须,因此在使用该框架时注意)
Map<String, T> convertedInstances = new LinkedHashMap<>();
for (Map.Entry<String, T> entry : instances.entrySet()) {
convertedInstances.put(entry.getKey(),
warpIfNecessary(entry.getValue()));
}
// 自定义排序
return sortByOrdered(convertedInstances);
}

/** 针对需要的Bean进行包装代理 */
private <T> T warpIfNecessary(T object) {
if (object != null) {
if ((ClassHystrixTargeter != null && ClassHystrixTargeter.isAssignableFrom(object.getClass())) ||
(ClassDefaultTargeter != null && ClassDefaultTargeter.isAssignableFrom(object.getClass()))) {
// 如果是 Targeter, 进行AOP代理
return (T)enhancement(object);
}
}
return object;
}

/** 对BeanMap进行排序 */
private <T> Map<String, T> sortByOrdered(Map<String, T> beans) {
if (beans != null && !beans.isEmpty()) {
List<Map.Entry<String, T>> sortList = new ArrayList<>(beans.entrySet());
//noinspection rawtypes
sortList.sort(new Comparator<Map.Entry<String, T>>() {
private final Comparator orderComparator = new OrderComparator();

@SuppressWarnings("unchecked")
@Override
public int compare(Map.Entry<String, T> o1, Map.Entry<String, T> o2) {
Object bean1 = null;
Object bean2 = null;
if (o1 != null) {
bean1 = o1.getValue();
}
if (o2 != null) {
bean2 = o2.getValue();
}
return orderComparator.compare(bean1, bean2);
}
});
Map<String, T> result = new LinkedHashMap<>();
sortList.forEach(entry -> result.put(entry.getKey(), entry.getValue()));
return result;
}
return beans;
}

/** 对 HystrixTargeter 执行增强代理 */
private Object enhancement(Object target) {
Object proxy = enhancementCache.get(target);
if (proxy == null) {
synchronized (this) {
proxy = enhancementCache.get(target);
if (proxy == null) {
Map<String, FeignHystrixContextRegister> feignHystrixContextRegisterBeans =
beanFactory.getBeansOfType(FeignHystrixContextRegister.class);
Enhancer enhancer = new Enhancer();
// 增强HystrixTargeter
enhancer.setSuperclass(target.getClass());
enhancer.setCallback((MethodInterceptor) (contextThis, method, args, methodProxy) -> {
if (method.getName().equals("target")) {
// args = FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target
Feign.Builder feign = (Feign.Builder) args[1];
if (feign != null) {
// 可选,对Feign请求拦截器进行排序(有需求的可以使用反射进行操作)
HystrixFeignHelper.sortRequestInterceptors(feign);
}
if (feign instanceof HystrixFeign.Builder) {
// 代理方法执行
return new HystrixFeignHelper((HystrixFeign.Builder)feign, feignHystrixContextRegisterBeans.values())
.target(args[0],
(HystrixFeign.Builder)args[1],
(FeignContext)args[2],
(Target.HardCodedTarget)args[3]);
}
}
return methodProxy.invoke(target, args);
});
proxy = enhancer.create();
enhancementCache.put(target, proxy);
}
}
}
return proxy;
}

// AOP缓存
private final Map<Object, Object> enhancementCache = new ConcurrentHashMap<>();
}

代理Feign.Builder

现在,我们已经拦截了Feign的获取,并返回我们代理的对象,而代理对象如何生成,代理逻辑如何构建,将是本小节讨论的重点。

Target

我们代理HystrixFeign.Builder核心是其build方法,返回Feign客户端,发起来自target方法:整个流程基本保持默认,即获取配置,调用HystrixFeign.Builderbuild方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
public <T> T target(Object factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) throws Throwable {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
// 我们只处理HystrixFeign
return feign.target(target);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
// 指定contextId
String contextId = Fields.getFieldValue(factory, "contextId", String.class);
// 服务名称
String name = Fields.getFieldValue(factory, "name", String.class);
// 失败处理
Class<?> fallback = Fields.getFieldValue(factory, "fallback", Class.class);
// 失败工厂
Class<?> fallbackFactory = Fields.getFieldValue(factory, "fallbackFactory", Class.class);
String clientName = StringUtils.isEmpty(contextId) ? name : contextId;
SetterFactory setterFactory = context.getInstance(name, SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
if (fallback != void.class) {
T fallbackInstance = getFromContext("fallback", clientName, context,
fallback, target.type());
return build(target, fallbackInstance);
}
if (fallbackFactory != void.class) {
FallbackFactory<? extends T> fallbackFactoryInstance = (FallbackFactory<? extends T>) getFromContext(
"fallbackFactory", clientName, context, fallbackFactory,
FallbackFactory.class);
return build(target, fallbackFactoryInstance);
}
// fallback未指定,保持默认
return feign.target(target);
}

Build

由于我们代理了HystrixFeign.Builder,而Feign客户端的构建需要调用Feign.Builderbuild方法(当然如果嫌麻烦可以使用反射实现build方法原流程),我们代理的HystrixFeign.Builder对象所见的access只能访问未指定fallbackbuild方法,因此需要构建一个hystrix build方法流程,为此我们使用MethodHandle获取super父级方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** HystrixFeign操作(兼容其他框架对HystrixFeign的定制化) */
static class HystrixFeignHelper extends Feign.Builder{
/** 被代理的Builder */
private final HystrixFeign.Builder delegate;
private final Collection<FeignHystrixContextRegister> contextRegisters;
/** 父类的invocationHandlerFactory方法 */
private static MethodHandle invocationHandlerFactoryMethod;
/** 父类的contract方法 */
private static MethodHandle contractMethod;
/** 父类的build方法 */
private static MethodHandle buildMethod;
/** 初始化反射 */
static void init() {
try {
Field impl_lookup = ReflectionUtils.findField(MethodHandles.Lookup.class, "IMPL_LOOKUP");
Objects.requireNonNull(impl_lookup).setAccessible(true);
// 查找super方法
MethodHandles.Lookup lookup = (MethodHandles.Lookup) impl_lookup.get(null);
// 以下均为super方法
invocationHandlerFactoryMethod = lookup.findSpecial(Feign.Builder.class,
"invocationHandlerFactory",
MethodType.methodType(Feign.Builder.class, InvocationHandlerFactory.class),
Feign.Builder.class);
contractMethod = lookup.findSpecial(Feign.Builder.class,
"contract",
MethodType.methodType(Feign.Builder.class, Contract.class),
Feign.Builder.class);
buildMethod = lookup.findSpecial(Feign.Builder.class,
"build",
MethodType.methodType(Feign.class),
Feign.Builder.class);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
public HystrixFeignHelper(HystrixFeign.Builder delegate, Collection<FeignHystrixContextRegister> contextRegisters) {
this.delegate = delegate;
this.contextRegisters = contextRegisters;
}
/**
* 代理的HystrixFeign.Builder target构建
*
* @param target target
* @param fallback fallback实例
* @param <T> 类型
* @return feign实例
* @throws Throwable 异常
*/
public <T> T build(Target<T> target, T fallback) throws Throwable {
return build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null)
.newInstance(target);
}
/**
* 代理的HystrixFeign.Builder target构建
*
* @param target target
* @param fallbackFactory fallback工厂
* @param <T> 类型
* @return feign实例
* @throws Throwable 异常
*/
public <T> T build(Target<T> target, FallbackFactory<? extends T> fallbackFactory) throws Throwable {
return build(fallbackFactory).newInstance(target);
}
/**
* 代理的HystrixFeign.Builder target构建
*
* @param nullableFallbackFactory 统一的fallback工厂
* @return feign实例
* @throws Throwable 异常
*/
public Feign build(final FallbackFactory<?> nullableFallbackFactory) throws Throwable {
SetterFactory setterFactory = Fields.getFieldValue(delegate, "setterFactory", SetterFactory.class);
Contract contract = Fields.getFieldValue(delegate, "contract", Contract.class);
invocationHandlerFactoryMethod.bindTo(delegate).invoke(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
// 使用自定义的InvocationHandler以传递上下文,仿照 feign.hystrix.HystrixInvocationHandler 即可
return new MyHystrixInvocationHandler(target,
dispatch, setterFactory,
nullableFallbackFactory,
contextRegisters);
}
});
contractMethod.bindTo(delegate).invoke(new HystrixDelegatingContract(contract));
return (Feign) buildMethod.bindTo(delegate).invoke();
}
}
/** Feign客户端fallback代理注册信息 */
static class FeignClientRegisterInfo {
private final String name;
private final String contextId;
private final Class<?> type;
public FeignClientRegisterInfo(String name, String contextId, Class<?> type) {
this.name = name;
this.contextId = contextId;
this.type = type;
}
}

配置覆写

Feign客户端的配置信息均来自 FeignClientFactoryBean,因此使用一个BeanFactoryPostProcessor来拦截处理配置部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Collection<?> beans = queryFeignClientFactoryBeans(beanFactory);
if (!CollectionUtils.isEmpty(beans)) {
Objects.requireNonNull(beans).forEach(this::processFeignClientFactoryBean);
} else {
log.warn("No FeignClientFactoryBeans found.");
}
}
/** 在beanFactory中查找所有FeignClient的FactoryBean实例 */
private Collection<?> queryFeignClientFactoryBeans(ConfigurableListableBeanFactory beanFactory) {
Map<String, ?> beansOfType = null;
try {
ClassLoader loader = Optional.ofNullable(beanFactory.getBeanClassLoader()).orElse(HystrixFeignClientFallbackProcess.class.getClassLoader());
// FeignClientFactoryBean 是 package-access 级别的类
Class<?> feignClientFactoryBeanClass = loader.loadClass("org.springframework.cloud.openfeign.FeignClientFactoryBean");
beansOfType = beanFactory.getBeansOfType(feignClientFactoryBeanClass);
} catch (Exception e) {
log.error("Error fetch all feign client factory beans.", e);
}
if (beansOfType != null) {
return beansOfType.values();
}
return null;
}
/**
* 注册失败工厂bean
*
* @param context feign的应用上下文
*/
private void registerFeignClientFallback(FeignContext context) {
// 我们定义一个全局fallback处理
FeignFallbackInvocation fallbackInvocation = beanFactory.getBean(FeignFallbackInvocation.class);
// 获取FeignContext的具名context方法
Method getContext = ReflectionUtils.findMethod(FeignContext.class, "getContext", String.class);
Objects.requireNonNull(getContext).setAccessible(true);
for (FeignClientRegisterInfo info : feignClientRegisterInfos) {
String contextId = info.contextId;
String name = info.name;
Class<?> type = info.type;
// 上下文名称
String feignClientName = StringUtils.isEmpty(contextId) ? name : contextId;
try {
// 获取当前feignClient的具名context
AnnotationConfigApplicationContext applicationContext = (AnnotationConfigApplicationContext)
getContext.invoke(context, feignClientName);
// 将失败工厂注册于此
applicationContext.registerBean(feignClientName + "FallbackFactory",
CommonFeignClientFallbackFactory.class,
() -> new CommonFeignClientFallbackFactory<>(type, fallbackInvocation));
} catch (ReflectiveOperationException e) {
log.error("Error register feign client fallback factory of [" + feignClientName + "]", e);
}
}
}
/**
* 处理FeignClientFactoryBean,设置自动失败工厂
*
* @param bean FeignClientFactoryBean
*/
private void processFeignClientFactoryBean(Object bean) {
try {
// feign客户端目标接口
Class<?> type = Fields.getFieldValue(bean, "type", Class.class);
// 服务名称
String name = Fields.getFieldValue(bean, "name", String.class);
// 指定contextId
String contextId = Fields.getFieldValue(bean, "contextId", String.class);
// 失败处理
Class<?> fallback = Fields.getFieldValue(bean, "fallback", Class.class);
// 失败工厂
Class<?> fallbackFactory = Fields.getFieldValue(bean, "fallbackFactory", Class.class);
if (fallback != void.class || fallbackFactory != void.class) {
// 用户定义了失败处理,则不进行代理
return;
}
// 使用统一的失败工厂
Fields.setFieldValue(bean, "fallbackFactory", CommonFeignClientFallbackFactory.class);
feignClientRegisterInfos.add(new FeignClientRegisterInfo(name, contextId, type));
} catch (Exception e) {
log.error("Process FeignClientFactoryBean error: " + bean, e);
}
}
/** 需要注册失败工厂的feign客户端信息 */
private final Collection<FeignClientRegisterInfo> feignClientRegisterInfos = new ArrayList<>();

/** 统一的失败工厂(使用代理) */
static class CommonFeignClientFallbackFactory<T> implements FallbackFactory<T> {
// 目标接口
private final Class<T> target;
// 失败处理
private final FeignFallbackInvocation fallbackInvocation;
public CommonFeignClientFallbackFactory(Class<T> target, FeignFallbackInvocation fallbackInvocation) {
this.target = target;
this.fallbackInvocation = fallbackInvocation;
}
@Override
@SuppressWarnings("unchecked")
public T create(Throwable cause) {
return (T) Proxy.newProxyInstance(target.getClassLoader(), new Class<?>[]{target}, (proxy, method, args) -> {
String name = method.getName();
switch (name) {
case "equals":
return CommonFeignClientFallbackFactory.this.equals(args[0]);
case "hashCode":
return CommonFeignClientFallbackFactory.this.hashCode();
case "toString":
return CommonFeignClientFallbackFactory.this.toString();
}
return fallbackInvocation.fallback(target, method, args, cause);
});
}
}

然后只需要实现我们定义的全局fallback即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Component
public class DefaultFeignFallbackInvocation implements FeignFallbackInvocation {

/** log recorder */
private static final Logger log = LoggerFactory.getLogger(DefaultFeignFallbackInvocation.class);

private static ObjectMapper objectMapper;
static {
objectMapper = new ObjectMapper();
// 反序列化时忽略json字符串中不存在的属性
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 允许转义字符
objectMapper.enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature());
}

@Override
public Object fallback(Class<?> targetInterface, Method remoteMethod, Object[] args, Throwable exception) {
log.error("Error Feign execute [" + remoteMethod.getName() + "] with arguments: " + Arrays.toString(args) + " at " + targetInterface.getName() + " cause: " + exception.getMessage(), exception);
if (RetResult.class.isAssignableFrom(remoteMethod.getReturnType())) {
try {
if (exception instanceof FeignException) {
// 客户端异常,尝试返回原始信息
String bodyString = ((FeignException) exception).contentUTF8();
if (!StringUtils.isEmpty(bodyString)) {
RetResult<?> result = objectMapper.readValue(bodyString, RetResult.class);
if (result != null) {
return result;
}
}
}
} catch (Exception ignored) {}
return RetResultBuilder.fail(RetCodes.RET_REQ_SERVER_ERROR);
}
throw new BusinessRuntimeException();
}
}

还可以实现自己的InvocationHandler,编写特定的请求处理逻辑。

最后,将其做成配置类,通过注解导入,需要使用时在任意配置类打上该注解开启全局fallback代理:

1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(HystrixFeignClientFallbackProcess.class)
public @interface EnableFeignClientFallbackProxy {
}

4.x版本实现调整

简化很多,只需import该配置即可生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
class OpenFeignClientsFallbackProcess implements
BeanFactoryPostProcessor,
BeanPostProcessor,
PriorityOrdered {

/** log recorder */
private static final Logger log = LoggerFactory.getLogger(OpenFeignClientsFallbackProcess.class);

private static final String FEIGN_CLIENT_FACTORY_ATTR = "feignClientsRegistrarFactoryBean";

private final List<FeignClientFallbackPenning> fallbackPenning = new ArrayList<>();

private BeanFactory beanFactory;

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
for (String name : beanFactory.getBeanDefinitionNames()) {
BeanDefinition definition = beanFactory.getMergedBeanDefinition(name);
if (Objects.equals(definition.getBeanClassName(), FeignClientFactoryBean.class.getName())) {
processEagerlyFeignClient(definition);
} else if (definition.hasAttribute(FEIGN_CLIENT_FACTORY_ATTR)) {
processLazilyFeignClient(definition);
}
}
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof FeignClientFactory context) {
registerFallbackFactory(context);
}
return bean;
}

private void processEagerlyFeignClient(BeanDefinition definition) {
String className = (String) definition.getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE);
MutablePropertyValues properties = definition.getPropertyValues();
Object fallback = properties.get("fallback");
Object fallbackFactory = properties.get("fallbackFactory");
if (noCustomizedFallback(fallback, fallbackFactory) && className != null) {
Class<?> type = ClassUtils.resolveClassName(className, null);
fallbackPenning.add(new FeignClientFallbackPenning((String)properties.get("name"),
(String)properties.get("contextId"),
type));
properties.add("fallbackFactory", ProxiedFeignClientFallbackFactory.class);
}
}

private void processLazilyFeignClient(BeanDefinition definition) {
FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) definition
.getAttribute(FEIGN_CLIENT_FACTORY_ATTR);
if (factoryBean == null) {
return;
}
if (noCustomizedFallback(factoryBean.getFallback(), factoryBean.getFallbackFactory())) {
Class<?> type = factoryBean.getType();
fallbackPenning.add(new FeignClientFallbackPenning(factoryBean.getName(), factoryBean.getContextId(), type));
factoryBean.setFallbackFactory(ProxiedFeignClientFallbackFactory.class);
}
}

private boolean noCustomizedFallback(Object fallback, Object fallbackFactory) {
return (fallback == null && fallbackFactory == null) || (fallback == void.class && fallbackFactory == void.class);
}


private void registerFallbackFactory(FeignClientFactory context) {
ObjectProvider<FeignFallbackInvocation> provider = beanFactory.getBeanProvider(FeignFallbackInvocation.class);
Method method = ReflectionUtils.findMethod(FeignClientFactory.class, "getContext", String.class);
if (method != null) {
method.setAccessible(true);
for (FeignClientFallbackPenning penning : fallbackPenning) {
String name = !StringUtils.hasText(penning.contextId()) ? penning.name() : penning.contextId();
try {
GenericApplicationContext appContext = (GenericApplicationContext) method.invoke(context, name);
appContext.registerBean(name + "FallbackFactory",
ProxiedFeignClientFallbackFactory.class,
() -> new ProxiedFeignClientFallbackFactory<>(penning.type(), provider));
log.info("registered feign client fallback of [{}]({})", name, penning.type());
} catch (IllegalAccessException | InvocationTargetException e) {
log.error("failed to register feign client fallback factory of [{}] ({}).", name, penning.type(), e);
}
}
} else {
log.error("unable to resolve reflection info of {}.", FeignClientFactory.class);
}

}



static class ProxiedFeignClientFallbackFactory<T> implements FallbackFactory<T> {
private final Class<T> targetType;
private final ObjectProvider<FeignFallbackInvocation> fallbackInvocation;

ProxiedFeignClientFallbackFactory(Class<T> targetType, ObjectProvider<FeignFallbackInvocation> fallbackInvocation) {
this.targetType = targetType;
this.fallbackInvocation = fallbackInvocation;
}

@SuppressWarnings("unchecked")
@Override
public T create(Throwable cause) {
FeignFallbackInvocation invocation = fallbackInvocation.getIfAvailable();
if (invocation == null) {
log.warn("Bean of type {} not found. feign client fallback failed at: {}", FeignFallbackInvocation.class, targetType);
}
return (T)Proxy.newProxyInstance(targetType.getClassLoader(), new Class[]{targetType}, (proxy, method, args) -> {
String name = method.getName();
return switch (name) {
case "equals" -> equals(args[0]);
case "hashCode" -> hashCode();
case "toString" -> toString();
default -> {
if (invocation != null) {
yield invocation.fallback(targetType, method, args, cause);
} else if (cause instanceof RuntimeException e) {
throw e;
} else {
throw new RuntimeException(cause);
}
}
};
});
}

@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (other == null) {
return false;
}
return super.equals(other);
}

@Override
public int hashCode() {
return super.hashCode() + fallbackInvocation.hashCode();
}
}

private record FeignClientFallbackPenning(String name, String contextId, Class<?> type){};

@Override
public int getOrder() {
return 0;
}
}

Feign客户端Fallback全局代理
https://vicasong.github.io/spring-cloud/feign-fallback-proxy/
作者
Vica
发布于
2020年6月15日
许可协议