IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Feign 应用之 RequestInterceptor 拦截器,超实用指南 -> 正文阅读

[Java知识库]Feign 应用之 RequestInterceptor 拦截器,超实用指南


前言

参考相关版本:

  • feign-core-10.10.1
  • spring-cloud-starter-openfeign:2.2.5.RELEASE

思考一下,你目前正在使用微服务体系,一个普通的用户请求可能会在微服务之间多次调用,而途径的每个微服务都需要原始请求的部分参数,你会如何传递这些参数?

在之前的文章中,我们了解到,Feign 的本质就是 JAVA 易用版的 HTTP 上层封装,本质还是 HTTP 调用,点击了解详情

想要原始请求参数在微服务之间流转,本质就是在调用下游服务的 HTTP 请求头上添加这些参数,最好还是业务逻辑无侵入性。

Feign 提供了拦截器机制,在真正 HTTP 调用之前,执行拦截器逻辑,你只需要实现特定的拦截器即可,业务逻辑层无感知。



一、使用

在使用上,我们主要从原生 Feign 和 Spring 体系下整合 Feign 来看具体的使用方式。

你需要注意的是,SpringCloud-OpenFeign 底层也是依赖于 Feign,只不过在使用上提供了一些便利而已。


1. Feign 使用:

原生 feign 的拦截器使用方式:

static class ForwardedForInterceptor implements RequestInterceptor {
  @Override public void apply(RequestTemplate template) {
    template.header("X-Forwarded-For", "origin.host.com");
  }
}

public class Example {
  public static void main(String[] args) {
    Bank bank = Feign.builder()
                 .decoder(accountDecoder)
                 .requestInterceptor(new ForwardedForInterceptor())
                 .target(Bank.class, "https://api.examplebank.com");
  }
}

简单的说,实现 RequestInterceptor 拦截器,并手动添加至 feignClient 实例中,在真实请求调用时发挥作用。

2. SpringCloudOpenFeign

如果你习惯使用 Spring 框架,那么 SpringCloud-OpenFeign 应该是首选,使用起来也是相当方便,主要有以下几种使用方式:

1)自定义 Bean:

@Configuration
public class FooConfiguration {
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }

    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("user", "password");
    }
}

这种是全局有效,会自动添加到所有 FeignClient 的拦截器列表中。


2)配置文件中指定拦截器:

我们除了创建拦截器 bean 实列外,还可以直接在 application.yml 配置文件中添加配置:

feign:
  client:
    config:
      feignName:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        errorDecoder: com.example.SimpleErrorDecoder
        retryer: com.example.SimpleRetryer
        requestInterceptors:
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        encoder: com.example.SimpleEncoder
        decoder: com.example.SimpleDecoder
        
        ...
        

这种方式,本质也是在扫描并初始化 FeignClient 的时候,创建拦截器实例,并添加到 FeignClient 实例中。

你也看到了,这种是应用于特定的 feignName,即特定的 FeignClient 实例。


3)FeignClient 的配置选择中指定拦截器:

@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
public interface FooClient {
    //..
}

拦截器应用范围,当前 FeignClient 实例。



3. 区别?

Feign 拦截器的使用非常简单:

  • 首先,实现 RequestInterceptor
  • 将实现类添加至 FeignClient 的实例中

值得注意的是,SpringCloud-OpenFeign 与原生 Feign 使用方式的主要区别在于,Spring 提供了自动扫描并创建 FeignClient 实例的能力,因此,其拦截器也是自动注入的

当然,本质还是原生 Feign 的使用方式而已。


二、原理

原理上我们主要了解,feign 拦截器何时发挥作用,以及 Spring 又是如何整合 Feign,我们从第一视角,了解完整的一条链路。

1. Feign

feign-core 核心包提供了动态代理类 SynchronousMethodHandler,该类是 feign 调用的核心处器,包括 http 调用、拦截器处理等等。

  Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    // 拦截器处理入口
    Request request = targetRequest(template);
    
    ...
    
    Response response;
    long start = System.nanoTime();
    try {
      response = client.execute(request, options);
      // ensure the request is set. TODO: remove in Feign 12
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    } 
    ...
    
}

是不是很熟悉?典型的 http 调用,在调用之前放置了拦截器处理逻辑。

  Request targetRequest(RequestTemplate template) {
    for (RequestInterceptor interceptor : requestInterceptors) {
      interceptor.apply(template);
    }
    return target.apply(template);
  }

就这样,我们在自定义拦截器的处理逻辑就被应用到了 http 请求过程中,下游直接从 http 请求中取就完事了。


2. SpringCloudOpenFeign

介绍完了拦截器如何应用于 http 请求中,接下来我们看看 Spring 体系下,拦截器如何被加载?

在原生 feign 使用过程中,拦截器是这样添加的:

    Bank bank = Feign.builder()
                 .decoder(accountDecoder)
                 .requestInterceptor(new ForwardedForInterceptor())
                 .target(Bank.class, "https://api.examplebank.com");

当然,Spring 有自己强大的 IOC 容器管理,为我们提供了更加方便且优雅的添加方式。

SpringCloud-OpenFeign 提供了注解 @FeignClient定义 feign 请求客户端,只要我们通过注解 @EnableFeignClients 开启 feign 客户端注解扫描,这些 client 就会被 Spring IOC 解析成 bean 并被管理起来。

我们知道 Spring 在创建 bean 的过程中,可以通过 配置、yaml 属性等解析 bean 的参数并注入,我们的拦截器也是这个时候被添加,对应了我们使用篇的几种方式。

关于 FeignClient bean 的创建,我们可以主要关注 FeignClientFactoryBean 类,见名思义,xxxFactoryBean 你懂得。

	protected void configureFeign(FeignContext context, Feign.Builder builder) {
		FeignClientProperties properties = applicationContext
				.getBean(FeignClientProperties.class);

		FeignClientConfigurer feignClientConfigurer = getOptional(context,
				FeignClientConfigurer.class);
		setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());

		if (properties != null && inheritParentContext) {
			if (properties.isDefaultToProperties()) {
			    // 从配置解析参数并注入 client bean
				configureUsingConfiguration(context, builder);
				configureUsingProperties(
						properties.getConfig().get(properties.getDefaultConfig()),
						builder);
				configureUsingProperties(properties.getConfig().get(contextId), builder);
			}
			else {
				configureUsingProperties(
						properties.getConfig().get(properties.getDefaultConfig()),
						builder);
				configureUsingProperties(properties.getConfig().get(contextId), builder);
				configureUsingConfiguration(context, builder);
			}
		}
		else {
			configureUsingConfiguration(context, builder);
		}
	}

值得注意的, feign 的拦截器可以定义多个。如果你定义了多个拦截器 bean 的话,都会被注入。

不过,如果你拦截器的 beanName 出现同名的话,会出现覆盖的情况。



总结

本文主要讲解了 feign 拦截器的几种主要应用方式、拦截器的工作入口等。

另外,在 SpringCloud 体系下,我们还介绍了拦截器如何被自动扫描并装配到 FeignClient 的 bean 实例中。

feign 拦截器的应用应该是非常广泛的,如果你使用的 SpringCloud 体系,应该更有感触。我们一般可以用来权限认证、请求头参数传递 …

在笔者的生产项目中,一般是将其放在公共包里,每个微服务项目直接依赖公共包,便可实现参数的上下游传递,十分方便!!!




相关参考:
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 00:38:26  更:2022-09-30 00:39:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年3日历 -2025/3/10 15:46:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码