作者:手机用户2602922981 | 来源:互联网 | 2023-10-10 20:58
起因:在工作中进行微服务开发过程中,为将接口及实现分离(便于提供API模块给其他微服务模块调用),将Feign客户端接口定义与Feign客户端实现分别写在API模块与服务模块中,由
起因:
在工作中进行微服务开发过程中,为将接口及实现分离(便于提供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类的包路径是否匹配。