feign.RequestInterceptor 简介-笔记

1. feign.RequestInterceptor 简介

Feign 是一个声明式 Web 服务客户端,用于简化 HTTP 请求的编写与管理。feign.RequestInterceptor 是 Feign 提供的一个接口,用于在请求发出之前对其进行拦截和修改。这在微服务架构中非常有用,比如在请求中统一添加认证头、日志追踪标识、设置自定义头等,从而提升代码的可维护性和一致性。

RequestInterceptor 接口的核心方法是:

java 复制代码
void apply(RequestTemplate template);

该方法会在每次 Feign 发起请求之前被调用。可以通过 RequestTemplate 对象修改请求的以下内容:

  • 添加或修改请求头(Headers)
  • 添加或修改请求参数(Query Parameters)
  • 修改请求体(Body)
  • 添加日志或追踪信息
  • 添加身份验证信息(如 Token、OAuth 令牌等)

2. RequestInterceptor使用示例

step1.创建 AuthRequestInterceptor。

该拦截器是一个简单的 RequestInterceptor 实现,用于在所有请求中添加认证 Token:

java 复制代码
import feign.RequestInterceptor;
import feign.RequestTemplate;

public class AuthRequestInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        // 添加认证 Token 到请求头
        template.header("Authorization", "Bearer your_access_token_here");
    }
}

step2. 将拦截器应用到 Feign Client

通过以下两种方式将 AuthRequestInterceptor 应用到你的 Feign Client 中:

方法一:通过 @FeignClient 注解配置

java 复制代码
import org.springframework.cloud.openfeign.FeignClient;

//注意:configuration 属性接受一个类或多个类,用于配置 Feign Client 的行为。
@FeignClient(
    name = "user-service",
    configuration = AuthRequestInterceptor.class
)
public interface UserClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable("id") Long id);
}

方法二:通过配置类注册为 Bean

先在配置类中注册 RequestInterceptor 为 Bean:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

    @Bean
    public RequestInterceptor authRequestInterceptor() {
        return new AuthRequestInterceptor();
    }
}

再在 @FeignClient 中引用配置类:

java 复制代码
@FeignClient(
    name = "user-service",
    configuration = FeignConfig.class
)
public interface UserClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable("id") Long id);
}

3. 一个完整的示例

3.1 场景说明

在微服务架构中,使用 Feign 作为 HTTP 客户端时,我们常常需要对请求进行统一处理。这些需求可以通过多个 RequestInterceptor 实现,并在 Feign 请求发送前依次执行。下面我们通过一个实际场景来演示如何配置多个 RequestInterceptor 并使用它们。

我们有一个名为 myWeather-service 的服务,它通过 Feign 调用 一个免费的天气服务weather-service 来获取天气信息。在调用过程中,我们需要:

  1. 添加认证 Token(如 JWT);
  2. 生成请求追踪 ID(用于链路追踪);
  3. 记录请求日志(便于调试与监控)。

3.2 项目结构

java 复制代码
src/main/java
├── config
│   └── FeignConfig.java
├── interceptor
│   ├── AuthRequestInterceptor.java
│   ├── TraceRequestInterceptor.java
│   └── LoggingRequestInterceptor.java
├── client
│   └── WeatherClient.java
└── MyWeatherController.java
│  
└── MyWeatherWebApplication.java

3.3 完整代码

step1. 定义多个 RequestInterceptor

AuthRequestInterceptor: 添加认证信息

java 复制代码
package com.example.interceptor;

import feign.RequestInterceptor;
import feign.RequestTemplate;

public class AuthRequestInterceptor implements RequestInterceptor {
    private final String authToken;

    public AuthRequestInterceptor(String authToken) {
        this.authToken = authToken;
    }

    @Override
    public void apply(RequestTemplate template) {
        template.header("Authorization", "userDefineAuthToken-" + authToken);
    }

}

TraceRequestInterceptor: 添加请求追踪 ID

java 复制代码
package com.example.interceptor;

import feign.RequestInterceptor;
import feign.RequestTemplate;
import java.util.UUID;

public class TraceRequestInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        String traceId = UUID.randomUUID().toString();
        template.header("X-Request-TraceId", traceId);
    }
}

LoggingRequestInterceptor:记录请求日志

java 复制代码
// LoggingInterceptor.java
package com.example.interceptor;

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoggingRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        Request request = template.request();
        // 记录请求信息
        System.out.println("┌───────────────────────────────────────────────────────────────────────");
        System.out.println("│ Sending request method: " + template.method());
        System.out.println("│ Sending request url: " + template.url());
        System.out.println("│ Sending request path: " + template.path());
        System.out.println("│ requestHeaders: ");
        for (String headerName : request.headers().keySet()) {
            System.out.println("│   " + headerName + ": " + request.headers().get(headerName));
        }
        System.out.println("└───────────────────────────────────────────────────────────────────────");
    }
}

step2.配置多个 RequestInterceptor

将多个拦截器注册为 Bean,并统一配置到 Feign Client 中:

java 复制代码
package com.example.config;

import com.example.interceptor.AuthInterceptor;
import com.example.interceptor.LogginInterceptor;
import com.example.interceptor.TraceInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {
    @Bean
    public AuthRequestInterceptor authRequestInterceptor() {
        return new AuthRequestInterceptor("authToken");
    }

    @Bean
    public TraceRequestInterceptor requestInterceptor() {
        return new TraceRequestInterceptor();
    }

    /**
     * Feign 默认按照 Bean 注册顺序执行 RequestInterceptor。
     * 将打印日志拦截器放到最后, 确保打印日志时能拿到 Auth 和 Trace 添加的请求头信息
     */
    @Bean
    public LoggingRequestInterceptor loggingRequestInterceptor() {
        return new LoggingRequestInterceptor();
    }
}

⚠️ 注意: Spring Boot 会自动将所有 RequestInterceptor 类型的 Bean 注册到 Feign 中,如无特殊需求无需手动注入。

step3. Feign Client 使用配置

这里使用FeignClient来调用免费的天气服务:

java 复制代码
package com.example.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(
    name = "weather-service",
    url = "http://t.weather.sojson.com/api/weather/city"
    //,configuration = FeignConfig.class //这里可选,Spring Boot 会自动将所有 RequestInterceptor 类型的 Bean 注册到 Feign 中,如无特殊需求无需手动注入
)
public interface WeatherClient {

    @GetMapping("/{cityId}")
    String getWeather(@PathVariable(value = "cityId") String cityId);
}

step4.MyWeatherController

java 复制代码
@RestController
public class MyWeatherController {

    @Autowired
    private WeatherClient weatherClient;

    @GetMapping("MyWeatherController/getWeather/{cityId}")
    public String getWeather(@PathVariable(value = "cityId") String cityId) {
        return weatherClient.getWeather(cityId);
    }
}

step5. MyWeatherWebApplication

java 复制代码
@SpringBootApplication
@EnableFeignClients(basePackages = {"com.example.*"})
@ComponentScan(basePackages = {"com.example.*"})
public class MyWeatherWebApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyWeatherWebApplication.class, args);
    }

}

step6.调用feign服务

开启MyWeatherWebApplication服务,浏览器输入:http://127.0.0.1:8080/MyWeatherController/getWeather/101030100,输出日志如下:

java 复制代码
┌───────────────────────────────────────────────────────────────────────
│ Sending request method: GET
│ Sending request url: /101030100
│ Sending request path: /101030100
│ requestHeaders: 
│   Authorization: [userDefineAuthToken-authToken]
│   X-Request-TraceId: [ec74d613-8f0e-442c-89cd-4a3197ad7ea0]
└───────────────────────────────────────────────────────────────────────

拦截器执行顺序说明

Feign 默认按照 Bean 注册顺序执行 RequestInterceptor。上面例子中,FeignConfig注册bean时,若将TraceRequestInterceptor放在LoggingRequestInterceptor之后, 打印日志时将不能拿到 Trace 添加的请求头信息:

java 复制代码
@Configuration
public class FeignConfig {
    @Bean
    public AuthRequestInterceptor authRequestInterceptor() {
        return new AuthRequestInterceptor("authToken");
    }

    /**
     * Feign 默认按照 Bean 注册顺序执行 RequestInterceptor。
     * 将TraceRequestInterceptor放在LoggingRequestInterceptor之后, 打印日志时将不能拿到 Trace 添加的请求头信息
     */
    @Bean
    public LoggingRequestInterceptor loggingRequestInterceptor() {
        return new LoggingRequestInterceptor();
    }

    @Bean
    public TraceRequestInterceptor requestInterceptor() {
        return new TraceRequestInterceptor();
    }
}

你可以通过 @Order 注解改变执行顺序,例如:

java 复制代码
@Order(1)
public class AuthRequestInterceptor implements RequestInterceptor { ... }


@Order(2)
public class TraceRequestInterceptor implements RequestInterceptor { ... }

3.4 何时仍需使用@FeignClient 配置

3.4.1 默认不对 @FeignClient 做配置

一般情况下,无需@FeignClient 中通过 configuration = FeignConfig.class 显式引用配置类。

只要 FeignConfig 正确地注册了 RequestInterceptor 等 Bean(如 AuthRequestInterceptor等),这些 Bean 就会被 Spring 容器管理,并且 全局生效。 Feign Client 在创建时会自动查找 Spring 容器中所有 RequestInterceptor 类型的 Bean,并按照注册顺序依次应用。因此,只要这些拦截器被正确注册为 Spring Bean,就不需要通过 @FeignClient(configuration = ...) 引用配置类

3.4.2 何时仍需使用@FeignClient 配置

虽然大多数情况下不需要显式引用配置类,但在以下场景中仍可能需要使用 @FeignClient(configuration = FeignConfig.class)

  1. 自定义 Feign Client 的配置需求

    • 如果某个 Feign Client 需要使用特定的配置类(比如不同的拦截器组合或覆盖默认配置),可以通过 configuration 属性指定。
    • 例如,某个 Feign Client 只需要 AuthInterceptor,而其他 Feign Client 需要 AuthInterceptor + LoggingInterceptor
  2. 控制 Bean 加载顺序

    • 如果拦截器的执行顺序非常重要,可以通过 @Order 注解或在配置类中显式控制顺序。
    • 使用 @FeignClient(configuration = FeignConfig.class) 可以确保配置类中的 Bean 被优先加载。
  3. 避免自动配置与手动配置冲突

    • 如果自动配置类和手动配置类中存在相同类型的 Bean(如多个 RequestInterceptor 实现),可能会导致冲突。此时可通过 @FeignClient(configuration = ...) 显式指定。

3.4.3 总结

场景 是否需要 @FeignClient(configuration = FeignConfig.class)
FeignConfig 已通过自动配置类注册到 Spring 容器 ❌ 不需要
某个 Feign Client 需要自定义拦截器组合 ✅ 需要
需要控制拦截器的执行顺序 ✅ 需要(结合 @Order 使用)
避免自动配置与手动配置冲突 ✅ 需要

实践建议

  • 优先使用自动配置 :如果所有 Feign Client 都需要相同的拦截器配置,直接通过自动配置类注册即可,无需每个 Feign Client 配置 configuration
  • 确保自动配置类正确注册 :检查 FeignConfig 是否已注册到 Spring 容器,并验证拦截器是否生效。
  • 按需定制配置 :如果某些 Feign Client 需要特殊处理,再通过 @FeignClient(configuration = ...) 显式指定。

4. 相关文档

FeignClient用法-笔记-CSDN博客

okhttp3.Interceptor简介-笔记-CSDN博客

相关推荐
慌糖6 分钟前
RabbitMQ:消息队列的轻量级王者
开发语言·javascript·ecmascript
笑衬人心。10 分钟前
Ubuntu 22.04 修改默认 Python 版本为 Python3 笔记
笔记·python·ubuntu
风象南13 分钟前
SpringBoot 控制器的动态注册与卸载
java·spring boot·后端
金色光环31 分钟前
【Modbus学习笔记】stm32实现Modbus
笔记·stm32·学习
醇醛酸醚酮酯31 分钟前
Qt项目锻炼——TODO清单(二)
开发语言·数据库·qt
jioulongzi36 分钟前
记录一次莫名奇妙的跨域502(badgateway)错误
开发语言·python
我是一只代码狗39 分钟前
springboot中使用线程池
java·spring boot·后端
hello早上好1 小时前
JDK 代理原理
java·spring boot·spring
PanZonghui1 小时前
Centos项目部署之Java安装与配置
java·linux
向阳@向远方1 小时前
第二章 简单程序设计
开发语言·c++·算法