关于动态创建Feign Client的问题

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.其他方法

貌似还有很多其他的方法可用的,仔细研究一下源码就可了。

好了撒花完结!

至于底层逻辑怎么走的,原理啊,那些,先自己研究一下代码吧,毕竟开源的,都是有料的。好好学习一下。转载码一下小尾巴哈

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值