Feign 动态服务名或者URL 解决No bean named问题

Feign 动态服务名或者URL 解决No bean named问题

动态Feign实现

实现主要依赖FeignClientBuilder,过程也和其他博客类似,但是注意关键位置的变化。

代码实现

一个工厂

// 这和教程没多大区别
@Component
public class DynamicFeignClient<T> {

    private FeignClientBuilder feignClientBuilder;

    private final ConcurrentHashMap<String, T> CACHE_BEAN = new ConcurrentHashMap();

    public DynamicFeignClient(@Autowired ApplicationContext appContext, @Autowired FeignContext feignContext) {
        this.feignClientBuilder = new FeignClientBuilder(appContext);
    }

    /**
     * 可以基于此方法,完成服务动态切换
     * @param type
     * @param serviceName 服务名
     * @return
     */
    public T getFeignClient(final Class<T> type, final String serviceName) {
        return getFeignClient(type, serviceName, null);
    }

    public T getFeignClient(final Class<T> type, final Class<?> fallbackFactory, final String serviceName) {
        return getFeignClient(type, fallbackFactory, serviceName, null);
    }

    public T getFeignClient(final Class<T> type, final FeignClientFactoryBean clientFactoryBean, final String serviceName) {
        return getFeignClient(type, clientFactoryBean, serviceName, null);
    }

    public T getFeignClient(final Class<T> type, String serviceName, final String serviceUrl) {
        String k = serviceName;
        if(StrUtil.isNotEmpty(serviceUrl)){
            k = serviceUrl;
        }
        return CACHE_BEAN.computeIfAbsent(k, (t)->{
            FeignClientBuilder.Builder<T> builder = this.feignClientBuilder.forType(type, serviceName);
            if(StrUtil.isNotEmpty(serviceUrl)){
                builder.url(serviceUrl);
            }
            return builder.build();
        });
    }

    public T getFeignClient(final Class<T> type, final Class<?> fallbackFactory, final String serviceName, final String serviceUrl) {
        String k = serviceName;
        if(StrUtil.isNotEmpty(serviceUrl)){
            k = serviceUrl;
        }
        return CACHE_BEAN.computeIfAbsent(k, (t)->{
            FeignClientFactoryBean feignClientFactoryBean = new FeignClientFactoryBean();
            feignClientFactoryBean.setFallbackFactory(fallbackFactory);
            FeignClientBuilder.Builder<T> builder = this.feignClientBuilder.forType(type, feignClientFactoryBean, serviceName);
            if(StrUtil.isNotEmpty(serviceUrl)){
                builder.url(serviceUrl);
            }
            return builder.build();
        });
    }

    public T getFeignClient(final Class<T> type, final FeignClientFactoryBean clientFactoryBean, final String serviceName, final String serviceUrl) {
        String k = serviceName;
        if(StrUtil.isNotEmpty(serviceUrl)){
            k = serviceUrl;
        }
        return CACHE_BEAN.computeIfAbsent(k, (t)->{
            FeignClientBuilder.Builder<T> builder = this.feignClientBuilder.forType(type, clientFactoryBean, serviceName);
            if(StrUtil.isNotEmpty(serviceUrl)){
                builder.url(serviceUrl);
            }
            return builder.build();
        });
    }
    
    private T getFromCache(final String serviceName, final String serviceUrl){
        if(StrUtil.isNotEmpty(serviceUrl)){
            return CACHE_BEAN.get(serviceUrl);
        }else {
            return CACHE_BEAN.get(serviceName);
        }
    }
}

一个示例接口

注意:常见博客都让删除这个注解,但是删除后会出现No bean named OpenAPI 异常,保留此注解,并将primary设置为false

primary=false 不是必须的,如果想将工厂构造的对象作为bean覆盖feign创建的bean则需要这个参数;

@FeignClient(
        fallbackFactory = RouteServiceAPIFallbackFactory.class,
        configuration = FeignConfiguration.class,
        primary = false
)
public interface OpenAPI {
	@GetMapping("select/demo")
    String selectDemo(@RequestParam(name = "name", required = false) Integer name);
}

覆盖bean(可选)

如果不想用新创建的结果覆盖feign创建的bean,则不必要。比如项目中会运行时动态切换服务名,则不适合将结果设置为bean。
本人项目只是想服务名受到配置文件控制。
@Configuration
@Slf4j
public class NotificationOpenConfiguration {

    @Value("${test.serverName:xxx-server}")
    private String serverName;

    @Value("${test.serverUrl:}")
    private String serverUrl;
    private final DynamicFeignClient<NotificationOpenAPI> client;

    @Bean
    @Primary
    public OpenAPI init(){
        log.debug("Init notification open api serverName={}, serverUrl={},如果serverUrl不为空,则优先使用serverUrl", serverName, serverUrl);
        OpenAPI openAPI = client.getFeignClient(OpenAPI.class, RouteServiceAPIFallbackFactory.class, serverName, serverUrl);
        return openAPI;
    }
}

猜测

根据代码追踪结果来看,FeignClientBuilder最终执行需要一个托管给spring的实现类{OpenAPI} 的bean,FeignClientBuilder会更具serverName或url判断是否需要创建新的{OpenAPI} 的bean,如果serverName或url无变化则返回原有Bean,否则创建一个新的实现。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值