Hi ~ 小老弟开始转公众号啦,欢迎大家来指点迷津呀
太久没写了,今天更一下 [手动滑稽]
好的正题:如何动态创建Feign Client~
众所周知,spring cloud.......懒得写了,直接堆代码吧
项目所用的feign是openfeign
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
一般来说,我们是直接定义一个interface,然后在上面 @FeignClient(value="xxx")
@FeignClient(value = "xxx")
public interface ITestFeign {
}
毕竟springcloud,如果是同一个接口,分散到n个服务当中,并且只有一个入口的时候,那么问题就来了:我是写对应的n个FeignClient还是写个模板动态构建FeignClient呢?
1. 常规办法:写对应的n个FeignClient。
2. 一个模板 + 动态构建!
首先,我们ctrl + 左键 点 @FeignClient 的时候可以直接进入FeignClient 这个注解里面,然后继续 ctrl + 左键 点 FeignClient 可以找到使用它的代码
让我们来聚焦FeignClientBuilder这个类,我们会发现他有三大块:applicationContext成员常量,forType方法,Builder内部类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.cloud.openfeign;
import feign.hystrix.FallbackFactory;
import org.springframework.context.ApplicationContext;
public class FeignClientBuilder {
private final ApplicationContext applicationContext;
public FeignClientBuilder(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public <T> FeignClientBuilder.Builder<T> forType(final Class<T> type, final String name) {
return new FeignClientBuilder.Builder(this.applicationContext, type, name);
}
public static final class Builder<T> {
private FeignClientFactoryBean feignClientFactoryBean;
private Builder(final ApplicationContext applicationContext, final Class<T> type, final String name) {
this.feignClientFactoryBean = new FeignClientFactoryBean();
this.feignClientFactoryBean.setApplicationContext(applicationContext);
this.feignClientFactoryBean.setType(type);
this.feignClientFactoryBean.setName(FeignClientsRegistrar.getName(name));
this.feignClientFactoryBean.setContextId(FeignClientsRegistrar.getName(name));
this.feignClientFactoryBean.setInheritParentContext(true);
this.url("").path("").decode404(false);
}
public FeignClientBuilder.Builder<T> url(final String url) {
this.feignClientFactoryBean.setUrl(FeignClientsRegistrar.getUrl(url));
return this;
}
public FeignClientBuilder.Builder<T> contextId(final String contextId) {
this.feignClientFactoryBean.setContextId(contextId);
return this;
}
public FeignClientBuilder.Builder<T> path(final String path) {
this.feignClientFactoryBean.setPath(FeignClientsRegistrar.getPath(path));
return this;
}
public FeignClientBuilder.Builder<T> decode404(final boolean decode404) {
this.feignClientFactoryBean.setDecode404(decode404);
return this;
}
public FeignClientBuilder.Builder<T> inheritParentContext(final boolean inheritParentContext) {
this.feignClientFactoryBean.setInheritParentContext(inheritParentContext);
return this;
}
public FeignClientBuilder.Builder<T> fallback(final Class<? extends T> fallback) {
FeignClientsRegistrar.validateFallback(fallback);
this.feignClientFactoryBean.setFallback(fallback);
return this;
}
public FeignClientBuilder.Builder<T> fallbackFactory(final Class<? extends FallbackFactory<? extends T>> fallbackFactory) {
FeignClientsRegistrar.validateFallbackFactory(fallbackFactory);
this.feignClientFactoryBean.setFallbackFactory(fallbackFactory);
return this;
}
public T build() {
return this.feignClientFactoryBean.getTarget();
}
}
}
众所周知,applicationContext 嘛,spring生态上下文,好东西一个,可以获取容器里面的bean,也可以手动将bean放进容器里面。
再接着看看,forType 方法,一个class类型的参数,一个string的参数,这不就很明显了嘛,那不就相当于一个构建FeignClient的工具类的方法了嘛。
Class<T> type 等于 ITestFeign ,String name 等于 @FeignClient 的 value 值:xxx
且不说里面的具体实现逻辑,人家都写好了工具类给你了,这都不会用就很尬了。
具体操作:
1. 模板
// 这里不需要@FeignClient注解
public interface IFeignClientTemplate {
// TODO 调用方法,可以写好一个方法,也可以通过反射去动态组装方法。
}
2.使用FeignClientBuilder去动态创建
下面贴上自己写的一个渣渣逻辑代码,简简单单,漏洞百出的那种,有大佬可以来指点迷津吗?
@Component
public class FeignClientUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
private static final Map<String, Object> BEAN_CACHE = new ConcurrentHashMap<>();
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (FeignClientUtils.applicationContext == null) {
FeignClientUtils.applicationContext = applicationContext;
}
}
public static <T> T build(String serverName, Class<T> targetClass) {
return buildClient(serverName, targetClass);
}
@SuppressWarnings("unchecked")
private static <T> T buildClient(String serverName, Class<T> targetClass) {
T t = (T) BEAN_CACHE.get(serverName);
if (Objects.isNull(t)) {
FeignClientBuilder.Builder<T> builder = new FeignClientBuilder(applicationContext).forType(targetClass, serverName);
t = builder.build();
BEAN_CACHE.put(serverName, t);
}
return t;
}
}
3.调用
IFeignClientTemplate feignClient = FeignClientUtils.build("xxx", IFeignClientTemplate.class);
4.其他方法
貌似还有很多其他的方法可用的,仔细研究一下源码就可了。
好了撒花完结!
至于底层逻辑怎么走的,原理啊,那些,先自己研究一下代码吧,毕竟开源的,都是有料的。好好学习一下。转载码一下小尾巴哈