事件回顾
起因
事情是这样的,最近在代码中需要使用 Feign 调用第三方服务。所以就是标准的一套操作:
-
- 引入第三方服务 Jar 包。
-
@EnableFeignClients
注解注入 Bean。
-
@Autowired
引入 Bean。
然后也没有在意到 Idea 已经有警告了,直接启动服务了。然后就出现如下错误:
Description:
Field api in com.xxx.service.impl.ServiceImpl required a bean of type 'com.xxx.api.Api' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.xxx.api.Api' in your configuration.
PS: Idea 的告警为 Could not autowire. No beans of 'Api' type found.
经过
然后结合 Idea 的警告与日志直接就配置 Autowired 的参数 @Autowired(required = false)
算是暂时解决了项目能启动以及 Idea 的警告。
但是。。。 但是。。。 但是。。。
在调试代码的时候出现一个奇怪的错误,引入的 Bean 居然为空,这让我感到异常困惑,加上对 Spring 不是很熟悉,让我顿感不安。
一顿百度、谷歌打断点调试,其中有个说的有道理
EnableFeignClients
扫描路径与ComponentScan
扫描冲突 检查后配置后发现没有问题。
直到查看到代码中关于两个 Feign 的导入才发现到了端倪。
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignClient;
终于问题的原因找到了项目中 FeignClient
与 EnableFeignClients
的版本不一致导致 Bean 注入失败。 @Autowired(required = false)
忽略了 Bean 的注入。最终导致NullPointerException
。
结果
本以为找到问题就可以解决了,但是面对第三方的 Jar 包,显然直接修改代码是不可能的。
那替换自己的呢?
那也是不行的,因为第四方服务、第五方服务都是 openfeign 的注解。
思来想去最后只能在自己的项目中基于 openfeign 的实现与原业务相同的接口。其实就是复制代码更改注解,其他的啥也不是。代码大致如下:
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@FeignClient( name = "service",
path = "/api/v1")
public interface ApiFeignClient {
@RequestMapping(
method = {RequestMethod.POST},
value = {"/query"},
consumes = {"application/json"}
)
ResponseBody query(@RequestBody RequestDto reqDto);
@RequestMapping(
method = {RequestMethod.GET},
value = {"/list"},
consumes = {"application/json"}
)
ResponseBody list(@RequestParam("name") String name);
}
然后代码中使用我们自己的 Bean,类似下面:
@Autowired
private ApiFeignClient api;
这样有缺点就是第三方服务的逻辑更改并不能同步到你的服务。所以这是一种后知后觉的方法去解决问题。本质上还是需要第三方服务实现基于 openfeign 的 Jar 包。
Feign 总结
经过这事之后,也查了很多资料,看了很多博客。这里总结一下关于 Feign 的知识点。
Feign 是什么?
Feign 是 Netflix 开发的声明式、模板化的HTTP客户端, Feign 可以帮助我们更快捷、优雅地调用 HTTP。
简单的说 Feign 通过将注解处理为模板化请求来工作。参数在输出之前直接应用于这些模板。
Netflixfeign 与 Openfeign 区别
Spring Cloud Feign是基于Netflix feign实现,整合了Spring Cloud Ribbon和Spring Cloud Hystrix,除了提供这两者的强大功能外,还提供了一种声明式的Web服务客户端定义的方式。
具体的可以参考 stackoverflow 的讨论。
注解的参数
主要说明 EnableFeignClients
与 FeignClient
。
EnableFeignClients
注解参数
名称 | 解释 |
---|---|
value | 为 basePackages 属性的别名,允许使用更简洁的书写方式。 |
basePackages | 设置自动扫描带有 @FeignClient 注解的基础包路径。 |
basePackageClasses | basePackages 属性的安全替代属性。扫描 @FeignClient修饰的类。 |
defaultConfiguration | 该属性用来自定义所有Feign客户端的配置,使用 @Configuration进行配置。 |
clients | 设置由 @FeignClient 注解修饰的类列表。注意 clients 不是空数组,则不通过类路径自动扫描功能来加载 FeignClient。 |
FeignClient
注解参数 | 名称 | 解释| | --- | --- | | name、value、serviceId | 三者作用基本一致。name指定 FeignClient 的名称,如果项目使用了 Ribbon,name属性会作为微服务的名称,用于服务发现。serviceId 已经废弃. | qualifier | 指定别名接本没用过。对应的是 @Qualifier注解 | | url | 指定 FeignClient 调用的地址,一般用于调试程序。 | | decode404 | 调用出现 http404 错误且该字段位为true,会调用decoder进行解码,否则抛出异常 | | configuration | Feign配置类,可以自定义Feign的 Encoder、Decoder、LogLevel、Contract。| | fallback| 定义容错的处理类,当调用远程接口异常时,会调用对应接口的异常逻辑,注意 fallback 指定的类必须实现 @FeignClient 标记的接口 | fallbackFactory| 用于生成fallback类示例,实现每个接口通用的容错逻辑,减少重复的代码。| | path | 定义当前 FeignClient 的统一前缀。| | primary| 是否将伪代理标记为主 Bean,默认为true。|