同一个工程多个注解@FeignClient(一个工程访问多个服务)

本文介绍了解决FeignClient在多次调用不同服务接口时出现404错误的方法。通过移除A、B接口上的@FeignClient注解,引入RemoteXxxClient类,并使用反射和自定义配置来实例化接口,有效避免了接口冲突问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述:

现有三个工程 A,B,C。  A工程会访问B工程接口,也会访问C工程接口,原来的做法是写好两个接口类去访问,如下所示,但是访问时出现问题,当第一次访问时,A调用B成功,A调用C成功。但第二次访问时,A调用B时,报错404

@FeignClient
public interface A{
    /**
     * 
     * @Title getConfigsOfService
     * @Description 获取服务的配置
     * @param name
     * @return
     * @return String
     */
    @GetMapping(value = "/data_control_web/ambari/service/getConfigsOfService")
    public String getConfigsOfService(@RequestParam("serviceName") String serviceName,
            @RequestParam("engineeringId") int engineeringId);

}
@FeignClient
public interface B{
    /**
     * 
     * @Title detailFileResourceVersion
     * @Description 获取文件详情
     * @param fileResourceId
     * @param version
     * @param engineeringId
     * @return
     * @return String
     */
    @GetMapping("/data_resource_manage/resource/file/version/detail")
    public String detailFileResourceVersion(@RequestParam("fileResourceId") int fileResourceId,
            @RequestParam("version") String version,
            @RequestParam("engineeringId") int engineeringId);
}

解决方法:

A,B接口去掉@FeignClient注解,再编写一个类RemoteXxxClient,通过反射方式注解两个接口,如下图所示

import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;


import feign.Contract;
import feign.Feign;
import feign.Request;
import feign.codec.Decoder;
import feign.codec.Encoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.http.MediaType;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Feign配置类,负责实例化所需要的伪客户端
 */
@Component
public class RemoteXxxClient {

    public final static String DATA_RESOURCE_MANAGER = "DATA-RESOURCE-MANAGER";
    public final static String DATA_CONTROL_WEB = "DATA-CONTROL-WEB";

    /**
     * FeignClientFactoryBean 该工厂类中 设置builder属性时就是通过该对象,源码中可看到
     */
    protected FeignContext feignContext;

    /**
     * 通过注入Eureka实例对象,就不用手动指定url,只需要指定服务名即可
     */
    protected EurekaClient eurekaClient;

    private static final Map<String, Object> FEIGN_CLIENTS = new ConcurrentHashMap<>();

    @Autowired
    public void init(final EurekaClient eurekaClient, final FeignContext feignContext) {
        this.eurekaClient = eurekaClient;
        this.feignContext = feignContext;
    }

    @Bean
    public A getQcloudService() {
        return create(A.class, DATA_CONTROL_WEB);
    }

    @Bean
    public B getTaskService() {
        return create(B.class, DATA_RESOURCE_MANAGER);
    }

    /**
     * 设置编码解码器为FastJson
     *
     * @param clazz
     * @param serverId
     * @param <T>
     * @return
     */
    protected <T> T create(Class<T> clazz, String serverId) {
        InstanceInfo nextServerFromEureka = eurekaClient.getNextServerFromEureka(serverId, false);
        String key = nextServerFromEureka.getIPAddr() + ":" + nextServerFromEureka.getPort() + ":"
                + clazz.getName();
        Object object = FEIGN_CLIENTS.get(key);
        if (Objects.isNull(object)) {
            object = Feign.builder()
                    // encoder指定对象编码方式
                    .encoder(this.feignEncoder())
                    // decoder指定对象解码方式
                    .decoder(this.feignDecoder())
                    // .client(feignClient)
                    // options方法指定连接超时时长及响应超时时长
                    .options(new Request.Options(5000, 5000))
                    // retryer方法指定重试策略
                    // .retryer(new Retryer.Default(5000, 5000, 3))
                    .contract(feignContext.getInstance(serverId, Contract.class))

                    // 自己写的类,传输token,不需要的话可以去掉
                    .requestInterceptor(new TokenConfig())
                    // target方法绑定接口与服务端地址。返回类型为绑定的接口类型。
                    .target(clazz, nextServerFromEureka.getHomePageUrl());
            FEIGN_CLIENTS.put(key, object);
        }

        return (T) object;
    }

    protected Encoder feignEncoder() {
        return new SpringEncoder(feignHttpMessageConverter());
    }

    protected Decoder feignDecoder() {
        return new SpringDecoder(feignHttpMessageConverter());
    }

    /**
     * 设置解码器为fastjson
     *
     * @return
     */
    private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
        final HttpMessageConverters httpMessageConverters =
                new HttpMessageConverters(this.getFastJsonConverter());
        return () -> httpMessageConverters;
    }

    private FastJsonHttpMessageConverter getFastJsonConverter() {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        List<MediaType> supportedMediaTypes = new ArrayList<>();
        MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_UTF8_VALUE);
        supportedMediaTypes.add(mediaTypeJson);
        converter.setSupportedMediaTypes(supportedMediaTypes);

        return converter;
    }
}

访问方式:

类中直接引用接口即可

@@Autowired
private  A a;

@@Autowired
private  B b;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值