微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

记一次SpringCloud微服务项目中Feign客户端创建失败问题排查

起因:

在工作中进行微服务开发过程中,为将接口及实现分离(便于提供API模块给其他微服务模块调用),将Feign客户端接口定义与Feign客户端实现分别写在API模块与服务模块中,由于以个人习惯定义包名,导致服务模块中创建Feign客户端失败,使用该客户端调用服务时,总会跳转到fallback定义(即服务降级)中。因为一开始没问题,修改了API模块及服务模块的包名之后,才出现问题,所以以这方面为思路去查找问题。

以下是代码复现(简约版) (注:本文后续代码仅展示关键部分)

feign客户端接口定义 -- 用户服务API模块
package com.personal.service.api.user.feign;

@FeignClient(
    value = "user-service",
    fallback = IUserClientFallback.class  # 启动后调用服务发现都往该实现类跳转了
)
public interface IUserClient {
}
feign客户端实现 -- 用户服务模块
package com.personal.service.user.feign;

@RestController
public class UserClient implements IUserClient {
}

 Application类定义 -- 用户服务模块

package com.personal.service.user

@SpringBootApplication
@EnableFeignClients
@EnablediscoveryClient
public class UserApplication {
}

过程:

通过debug排查后,发现是Feign客户端创建失败。故而调用服务时,都会执行服务降级实现。

然后在同事的提醒下,参考之前的项目,将UserApplication类移到了包com.personal.service下之后,Feign客户端创建成功。

虽然问题解决,但是我不太明白问题的根本原因是什么,所以进行了一番探究学习。

发现与Application类的路径有关后,在Spring boot的官方文档中查找Spring boot关于Application类的规定及推荐。发现了这一句:

 意思是:@SpringBootApplication注解通常会被我们放在主类中,它隐含为项目定义了基本的搜索包。即,Spring boot项目通常会以这个注解所在的类的包路径为根去扫描bean定义(如果我们没有另外指定时)。

代码中,我的@SpringBootApplication注解在UserApplication中。

然后,通过对@EnableFeignClients的实现进行debug代码追踪:

该注解通过@Import注解引入了FeignClientsRegistrar类,并在其中完成Feign客户端bean的注册工作。

@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
}

进入到FeignClientsRegistrar类,其实现了ImportBeanDeFinitionRegistrar接口,用于额外注册bean。找到注册bean定义的方法

@Override
public void registerBeanDeFinitions(AnnotationMetadata Metadata, BeanDeFinitionRegistry registry) {
        registerDefaultConfiguration(Metadata, registry);
        registerFeignClients(Metadata, registry);
}

找到注册Feign客户端的方法: registerFeignClients(Metadata, registry);

 进入到 getBasePackages(Metadata) 方法

所以会获取到UserApplication的包路径:com.personal.service.user,以该路径去扫描@FeignClient注解定义的Fegin客户端接口类。

但是我项目中的接口类路径为:com.personal.service.api.user.feign (文章一开始的代码复现中),故而找不到定义。Feign创建失败。至此,问题的根本原因找到。

结论:

使用Spring boot开发项目时,虽然提供给了我们很多便利,担其便捷的背后,也有着许多隐含的配置要求,所以我们要注意@SpringBootApplication注解定义的类的路径,在进行spring组件,即@Component、@Service等注解的bean时,如果我们未显式指定扫描包路径时,要注意定义的bean的包路径与Application类的包路径是否匹配。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐