SpringCloud升级之路2020.0.x版-29.Spring Cloud OpenFeign 的解析(1)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: SpringCloud升级之路2020.0.x版-29.Spring Cloud OpenFeign 的解析(1)

image.png


本系列代码地址: 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<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();
@Autowired(required = false)
private List<FeignFormatterRegistrar> 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 就是 SpringMvcContractSpringMvcContract 主要包含两部分核心逻辑:

  • 定义 Feign Client 专用的 Formatter 与 Converter 注册
  • 使用 AnnotatedParameterProcessor 来解析 SpringMVC 注解以及我们自定义的注解


定义 Feign Client 专用的 Formatter 与 Converter 注册

首先,Spring 提供了类型转换机制,其中单向的类型转换为实现 Converter 接口;在 web 应用中,我们经常需要将前端传入的字符串类型的数据转换成指定格式或者指定数据类型来满足我们调用需求,同样的,后端开发也需要将返回数据调整成指定格式或者指定类型返回到前端页面(在 Spring Boot 中已经帮我们做了从 json 解析和返回对象转化为 json,但是某些特殊情况下,比如兼容老项目接口,我们还可能使用到),这个是通过实现 Formatter 接口实现。举一个简单的例子:

定义一个类型:

@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<String, Student> {
  @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<Student> {
  @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 = 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<String> query = context.setTemplateParameter(name, data.template().queries().get(name));
  //将 RequestParam 放入 方法元数据 MethodMetadata
  data.template().query(name, query);
  //返回解析成功
  return true;
}

我们也可以实现 AnnotatedParameterProcessor 来自定义我们的注解,配合 SpringMVC 的注解一起使用去定义 FeignClient

目录
打赏
0
0
0
0
33
分享
相关文章
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
98 1
Spring Cloud Alibaba与Spring Cloud区别和联系?
Spring Cloud Alibaba与Spring Cloud区别和联系?
🛡️Spring Boot 3 整合 Spring Cloud Gateway 工程实践
本文介绍了如何使用Spring Cloud Alibaba 2023.0.0.0技术栈构建微服务网关,以应对微服务架构中流量治理与安全管控的复杂性。通过一个包含鉴权服务、文件服务和主服务的项目,详细讲解了网关的整合与功能开发。首先,通过统一路由配置,将所有请求集中到网关进行管理;其次,实现了限流防刷功能,防止恶意刷接口;最后,添加了登录鉴权机制,确保用户身份验证。整个过程结合Nacos注册中心,确保服务注册与配置管理的高效性。通过这些实践,帮助开发者更好地理解和应用微服务网关。
151 0
🛡️Spring Boot 3 整合 Spring Cloud Gateway 工程实践
|
3月前
|
Spring底层架构核心概念解析
理解 Spring 框架的核心概念对于开发和维护 Spring 应用程序至关重要。IOC 和 AOP 是其两个关键特性,通过依赖注入和面向切面编程实现了高效的模块化和松耦合设计。Spring 容器管理着 Beans 的生命周期和配置,而核心模块为各种应用场景提供了丰富的功能支持。通过全面掌握这些核心概念,开发者可以更加高效地利用 Spring 框架开发企业级应用。
111 18
AI 时代:从 Spring Cloud Alibaba 到 Spring AI Alibaba
本次分享由阿里云智能集团云原生微服务技术负责人李艳林主讲,主题为“AI时代:从Spring Cloud Alibaba到Spring AI Alibaba”。内容涵盖应用架构演进、AI agent框架发展趋势及Spring AI Alibaba的重磅发布。分享介绍了AI原生架构与传统架构的融合,强调了API优先、事件驱动和AI运维的重要性。同时,详细解析了Spring AI Alibaba的三层抽象设计,包括模型支持、工作流智能体编排及生产可用性构建能力,确保安全合规、高效部署与可观测性。最后,结合实际案例展示了如何利用私域数据优化AI应用,提升业务价值。
287 4
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
智慧工地云平台的技术架构解析:微服务+Spring Cloud如何支撑海量数据?
慧工地解决方案依托AI、物联网和BIM技术,实现对施工现场的全方位、立体化管理。通过规范施工、减少安全隐患、节省人力、降低运营成本,提升工地管理的安全性、效率和精益度。该方案适用于大型建筑、基础设施、房地产开发等场景,具备微服务架构、大数据与AI分析、物联网设备联网、多端协同等创新点,推动建筑行业向数字化、智能化转型。未来将融合5G、区块链等技术,助力智慧城市建设。
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
71 6
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
262 5
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
102 5

热门文章

最新文章