feign Ambiguous mapping 解决方式

Feign Ambiguous mapping 解决方式

背景

feign在使用过程中,一般先定义接口,再定义具体实现的controller,如下所示

@FeignClient(name = "shore-app")
public interface StoreApi {

    @GetMapping("/api/store/getStoreNum")
    Integer getStoreNum();

    @GetMapping("/api/store/getStoreNumByCondition1")
    Integer getStoreNumByCondition1();


    @GetMapping("/api/store/getStoreNumByCondition2")
    Integer getStoreNumByCondition2();
}

实现定义

@RestController
public class StoreController implements StoreApi {


    @Override
    public Integer getStoreNum() {
        return Integer.MAX_VALUE;
    }

    @Override
    public Integer getStoreNumByCondition1() {
        return Integer.MAX_VALUE;
    }

    @Override
    public Integer getStoreNumByCondition2() {
        return Integer.MAX_VALUE;
    }
}

这种方式去提供Feign给其他模块调用也是比较简洁的,不过在定义controller很多人做法是把url公共的部分抽取出来,如下

@FeignClient(name = "shore-app")
@RequestMapping("/api/store")
public interface StoreApi {

    @GetMapping("/getStoreNum")
    Integer getStoreNum();

    @GetMapping("/getStoreNumByCondition1")
    Integer getStoreNumByCondition1();


    @GetMapping("/getStoreNumByCondition2")
    Integer getStoreNumByCondition2();
}

然后会发现项目启动报错,有相同的url路径,启动不起来,报错如下

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'org.github.hoorf.store.api.StoreApi' method 
org.github.hoorf.store.api.StoreApi#getStoreNumByCondition2()
to {GET /api/store/getStoreNumByCondition2}: There is already 'storeController' bean method
org.github.hoorf.store.controller.StoreController#getStoreNumByCondition2() mapped.
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796) ~[spring-beans-5.2.5.RELEASE.jar:5.2.5.RELEASE]

报错原因是@RequestMapping标注上,被spring识别为controller


本文介绍的是一种新的feign定义模式去解决这问题,既能保持原有的controller使用习惯,又能将feign定义的接口复用到其他项目

实现

工程目录分包依赖情况

e1
|--e1-api
|     |--e1-store-api
|     |--e1-account-api
|     |--e1-common
|--e1-acount
|--e1-store     

其中e1-account,e1-store分别依赖e1-api,e1-api为所有api的聚合依赖

  1. 自定义Feign注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@FeignClient
@Conditional(AdaptiveFeignCondition.class)
public @interface AdaptiveFeign {

    @AliasFor(annotation = FeignClient.class)
    String value() default "";

    @AliasFor(annotation = FeignClient.class)
    Class<?> fallback() default void.class;

    @AliasFor(annotation = FeignClient.class)
    Class<?> fallbackFactory() default void.class;
}
  1. 定义注解启用条件

基本逻辑就是,如果识别到实现类有controller则不启用Feign注解,识别不到则启用Feign注解

@Slf4j
public class AdaptiveFeignCondition extends SpringBootCondition {
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        ClassMetadata classMetadata = (ClassMetadata) metadata;

        String className = classMetadata.getClassName();
        Class interfaceClass = null;
        try {
            interfaceClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
        }

        Set<Class<?>> classSet = ReflectionUtils.getSubTypesOf(interfaceClass);

        for (Class<?> implClass : classSet) {
            Controller annotation = AnnotationUtils.findAnnotation(implClass, Controller.class);
            if (null != annotation) {
               // log.debug("{} exits Controller.class ,no init feign {}", implClass.getName(), interfaceClass.getName());
                return ConditionOutcome.noMatch(new StringBuffer(implClass.getName()).append(" exits implement").toString());
            }
        }
        return ConditionOutcome.match();
    }
}

效果

account模块中使用

    @Autowired
    private AccountApi accountApi;

注入的是Spring中的AccountApi具体实现,也就是controller,走的是内部调用

store模块中使用
注入的是AccountApiFeign的实现,走的是远程调用

具体工程代码https://github.com/hoorf/arch/tree/master/arch-example-1

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Feign是一个基于注解的声明式HTTP客户端,它可以简化服务之间的远程调用。使用Feign可以像调用本地方法一样调用远程服务。 要使用Feign进行远程调用,需要进行以下几个步骤: 1. 添加依赖:在项目的pom.xml文件中添加Feign的依赖。例如,对于Spring Boot项目,可以添加以下依赖: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> ``` 2. 创建Feign客户端接口:定义一个Java接口,使用`@FeignClient`注解指定要调用的远程服务的名称。在接口中定义需要调用的方法,并使用`@RequestMapping`等注解指定远程服务的URL和参数。 ```java @FeignClient(name = "remote-service") public interface RemoteServiceClient { @RequestMapping(method = RequestMethod.GET, value = "/api/resource") String getResource(); } ``` 3. 配置Feign客户端:在Spring Boot应用程序的配置文件中,可以配置Feign客户端的一些属性,如远程服务的URL、连接超时时间等。例如: ```yaml spring: application: name: my-application cloud: feign: client: config: remote-service: connect-timeout: 5000 read-timeout: 5000 ``` 4. 使用Feign客户端:在需要调用远程服务的地方,通过依赖注入的方式使用Feign客户端接口,并调用定义的方法即可。 ```java @Autowired private RemoteServiceClient remoteServiceClient; public void doRemoteCall() { String resource = remoteServiceClient.getResource(); // 处理返回的资源数据 } ``` 通过以上步骤,就可以使用Feign进行远程调用了。Feign会根据接口定义自动生成对应的HTTP请求,并将结果转换为指定类型返回。同时,Feign还提供了负载均衡、熔断等功能,可以更方便地实现微服务架构中的服务调用。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值