feign.RequestInterceptor 简介-笔记

1. feign.RequestInterceptor 简介

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

RequestInterceptor 接口的核心方法是:

void apply(RequestTemplate template);

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

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

2. RequestInterceptor使用示例

step1.创建 AuthRequestInterceptor。

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

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 注解配置

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:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

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

再在 @FeignClient 中引用配置类:

@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 项目结构

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: 添加认证信息

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

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:记录请求日志

// 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 中:

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来调用免费的天气服务:

  • 天气API 数据(以天津为例),链接为:http://t.weather.sojson.com/api/weather/city/101030100
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

@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

@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,输出日志如下:

┌───────────────────────────────────────────────────────────────────────
│ 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 添加的请求头信息:

@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 注解改变执行顺序,例如:

@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博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值