SpringCloud升级之路2020.0.x版-29.Spring|SpringCloud升级之路2020.0.x版-29.Spring Cloud OpenFeign 的解析(1)
文章图片
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent在使用云原生的很多微服务中,比较小规模的可能直接依靠云服务中的负载均衡器进行内部域名与服务映射,通过健康检查接口判断实例健康状态,然后直接使用 OpenFeign 生成对应域名的 Feign Client。Spring Cloud 生态中,对 OpenFeign 进行了封装,其中的 Feign Client 的各个组件,也是做了一定的定制化,可以实现在 OpenFeign Client 中集成服务发现与负载均衡。在此基础上,我们还结合了 Resilience4J 组件,实现了微服务实例级别的线程隔离,微服务方法级别的断路器以及重试。
我们先来分析下 Spring Cloud OpenFeign
Spring Cloud OpenFeign 解析 从 NamedContextFactory 入手
Spring Cloud OpenFeign 的 github 地址:https://github.com/spring-cloud/spring-cloud-openfeign
首先,根据我们之前分析 spring-cloud-loadbalancer 的流程,我们先从继承
NamedContextFactory
的类入手,这里是 FeignContext
,通过其构造函数,得到其中的默认配置类:FeignContext.java
public FeignContext() {
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}
从构造方法可以看出,默认的配置类是:
FeignClientsConfiguration
。我们接下来详细分析这个配置类中的元素,并与我们之前分析的 OpenFeign 的组件结合起来。负责解析类元数据的 Contract,与 spring-web 的 HTTP 注解相结合
为了开发人员更好上手使用和理解,最好能实现使用 spring-web 的 HTTP 注解(例如
@RequestMapping
,@GetMapping
等等)去定义 FeignClient 接口。在 FeignClientsConfiguration
中就是这么做的:FeignClientsConfiguration.java
@Autowired(required = false)
private FeignClientProperties feignClientProperties;
@Autowired(required = false)
private List parameterProcessors = new ArrayList<>();
@Autowired(required = false)
private List feignFormatterRegistrars = new ArrayList<>();
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
boolean decodeSlash = feignClientProperties == null || feignClientProperties.isDecodeSlash();
return new SpringMvcContract(this.parameterProcessors, feignConversionService, decodeSlash);
}@Bean
public FormattingConversionService feignConversionService() {
FormattingConversionService conversionService = new DefaultFormattingConversionService();
for (FeignFormatterRegistrar feignFormatterRegistrar : this.feignFormatterRegistrars) {
feignFormatterRegistrar.registerFormatters(conversionService);
}
return conversionService;
}
其核心提供的 Feign 的 Contract 就是
SpringMvcContract
,SpringMvcContract
主要包含两部分核心逻辑:- 定义 Feign Client 专用的 Formatter 与 Converter 注册
- 使用 AnnotatedParameterProcessor 来解析 SpringMVC 注解以及我们自定义的注解
定义一个类型:
@Data
@AllArgsConstructor
public class Student {
private final Long id;
private final String name;
}
我们定义可以通过字符串解析出这个类的对象的 Converter,例如 "1,zhx" 就代表 id = 1 并且 name = zhx:
public class StringToStudentConverter implements Converter {
@Override
public Student convert(String from) {
String[] split = from.split(",");
return new Student(
Long.parseLong(split[0]),
split[1]);
}
}
然后将这个 Converter 注册:
@Configuration(proxyBeanMethods = false)
public class TestConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToStudentConverter());
}
}
编写一个测试接口:
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/string-to-student")
public Student stringToStudent(@RequestParam("student") Student student) {
return student;
}
}
调用
/test/string-to-student?student=1,zhx
,可以看到返回:{
"id": 1,
"name": "zhx"
}
同样的,我们也可以通过 Formatter 实现:
public class StudentFormatter implements Formatter {
@Override
public Student parse(String text, Locale locale) throws ParseException {
String[] split = text.split(",");
return new Student(
Long.parseLong(split[0]),
split[1]);
} @Override
public String print(Student object, Locale locale) {
return object.getId() + "," + object.getName();
}
}
然后将这个 Formatter 注册:
@Configuration(proxyBeanMethods = false)
public class TestConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new StudentFormatter());
}
}
Feign 也提供了这个注册机制,为了和 spring-webmvc 的注册机制区分开,使用了 FeignFormatterRegistrar 继承了 FormatterRegistrar 接口。然后通过定义
FormattingConversionService
这个 Bean 实现 Formatter 和 Converter 的注册。例如:假设我们有另一个微服务需要通过 FeignClient 调用上面这个接口,那么就需要定义一个 FeignFormatterRegistrar 将 Formatter 注册进去:
@Bean
public FeignFormatterRegistrar getFeignFormatterRegistrar() {
return registry -> {
registry.addFormatter(new StudentFormatter());
};
}
之后我们定义 FeignClient:
@FeignClient(name = "test-server", contextId = "test-server")
public interface TestClient {
@GetMapping("/test/string-to-student")
Student get(@RequestParam("student") Student student);
}
在调用 get 方法时,会调用 StudentFormatter 的 print 将 Student 对象输出为格式化的字符串,例如
{"id": 1,"name": "zhx"}
会变成 1,zhx
。AnnotatedParameterProcessor 来解析 SpringMVC 注解以及我们自定义的注解
AnnotatedParameterProcessor
是用来将注解解析成 AnnotatedParameterContext
的 Bean,AnnotatedParameterContext
包含了 Feign 的请求定义,包括例如前面提到的 Feign 的 MethodMetadata
即方法元数据。默认的 AnnotatedParameterProcessor
包括所有 SpringMVC 对于 HTTP 方法定义的注解对应的解析,例如 @RequestParam
注解对应的 RequestParamParameterProcessor
:RequestParamParameterProcessor.java
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
//获取当前参数属于方法的第几个
int parameterIndex = context.getParameterIndex();
//获取参数类型
Class> parameterType = method.getParameterTypes()[parameterIndex];
//要保存的解析的方法元数据 MethodMetadata
MethodMetadata data = https://www.it610.com/article/context.getMethodMetadata();
//如果是 Map,则指定 queryMap 下标,直接返回
//这代表一旦使用 Map 作为 RequestParam,则其他的 RequestParam 就会被忽略,直接解析 Map 中的参数作为 RequestParam
if (Map.class.isAssignableFrom(parameterType)) {
checkState(data.queryMapIndex() == null,"Query map can only be present once.");
data.queryMapIndex(parameterIndex);
//返回解析成功
return true;
}
RequestParam requestParam = ANNOTATION.cast(annotation);
String name = requestParam.value();
//RequestParam 的名字不能是空
checkState(emptyToNull(name) != null, "RequestParam.value() was empty on parameter %s", parameterIndex);
context.setParameterName(name);
Collection query = context.setTemplateParameter(name, data.template().queries().get(name));
//将 RequestParam 放入 方法元数据 MethodMetadata
data.template().query(name, query);
//返回解析成功
return true;
}
我们也可以实现
AnnotatedParameterProcessor
来自定义我们的注解,配合 SpringMVC 的注解一起使用去定义 FeignClient微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:【SpringCloud升级之路2020.0.x版-29.Spring|SpringCloud升级之路2020.0.x版-29.Spring Cloud OpenFeign 的解析(1)】
文章图片
推荐阅读
- jhipster|jhipster 升级无效问题
- 定投,开启个人品牌之路
- gitlab|Gitlab升级(12.2.1到14.6.4)
- GIS跨界融合赋能多领域技术升级,江淮大地新应用成果喜人
- 财富自由之路
- 《通往财富自由之路》人生的终极问题到底是什么()
- 读李笑来老师的“财富自由之路”7/365
- 读《财富自由之路》有感
- 99_读|99_读 被讨厌的勇气_岸见一郎
- 复盘学习《通往财富自由之路》专栏——“我们重新理解了付费”