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 {
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; } }
|