1. 拦截器要解决什么问题?
-
把签名、打点、鉴权、灰度、审计等横切逻辑从业务请求中抽离;
-
低侵入接入任意 OkHttpClient;
-
能通过运行时配置(如开关、密钥、目标地址等)动态生效;
-
支持"是否命中某个条件才生效"的灰度能力。
2. 自定义一个 OkHttp Interceptor
下方仅为示例:拦截器在命中某个"特征"时,将请求"包一层"再转发;未命中则直通。
java
public final class DemoInterceptor implements Interceptor {
private final DemoRuntimeSwitch runtime; // 运行时开关 & 配置
public DemoInterceptor(DemoRuntimeSwitch runtime) {
this.runtime = runtime;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// 1) 未开启或配置不完整:放行
if (!runtime.isOn() || !runtime.isConfigReady()) {
return chain.proceed(original);
}
// 2) 命中某个触发条件(Demo:URL 上带 trigger=true)
boolean hit = "true".equalsIgnoreCase(original.url().queryParameter("trigger"));
if (!hit) return chain.proceed(original);
// 3) 包装:示意将原请求信息转成一个 JSON 文本
String wrapped = DemoJson.wrap(original); // {method, path, headers, query, body}
// 4) 生成一个示意"签名"字符串(真实场景请用更安全算法与协议)
String sign = DemoSign.simple(runtime.getAppKey(), runtime.getSecret(), wrapped);
// 5) 构造"第二跳"请求(Demo:POST 表单提交到 runtime.getSecondHopUrl())
Request secondHop = new Request.Builder()
.url(runtime.getSecondHopUrl())
.post(new FormBody.Builder()
.add("appKey", runtime.getAppKey())
.add("payload", wrapped)
.add("sign", sign)
.build())
.build();
try {
return chain.proceed(secondHop);
} catch (Exception ex) {
// 6) 兜底:返回一个 Demo 错误响应(生产可返回统一错误结构)
return DemoResponse.errorJson(500, "second-hop failed: " + ex.getMessage());
}
}
}
要点回顾:
触发条件外置,真实可换成 token、Header、白名单等;
直通优先:未命中一律放行,降低对主链路影响;
二跳独立配置:URL、超时、重试策略等在配置中统一管理。
3. 运行时配置开关(Demo)
java
//只展示结构与意图
public final class DemoRuntimeSwitch {
private volatile boolean on;
private volatile String appKey;
private volatile String secret;
private volatile String secondHopUrl;
public boolean isOn() { return on; }
public boolean isConfigReady() {
return appKey != null && !appKey.isEmpty() &&
secret != null && !secret.isEmpty() &&
secondHopUrl != null && !secondHopUrl.isEmpty();
}
// 可由配置中心/管理接口更新
public String getAppKey() { return appKey; }
public String getSecret() { return secret; }
public String getSecondHopUrl() { return secondHopUrl; }
}
-
实际可用
AtomicReference
持有完整配置快照; -
更新方式可来自配置中心/Redis/管理接口,做到秒级生效;
-
配置校验应集中在一个地方(例如
normalizeAndValidate()
)。
4. 在 Spring Boot 中注入拦截器(Demo)
方式 A:直接注册为 Bean
java
@Configuration
public class DemoInterceptorConfig {
@Bean
public DemoRuntimeSwitch demoRuntimeSwitch() { return new DemoRuntimeSwitch(); }
@Bean
public Interceptor demoInterceptor(DemoRuntimeSwitch runtime) {
return new DemoInterceptor(runtime);
}
}
方式 B:自动收集所有拦截器,再统一挂载(可插拔)
java
// 一个简单的"桥",把容器中的拦截器收集起来,统一挂载到 OkHttp
public final class DemoInterceptorBridge {
private static final CopyOnWriteArrayList<Interceptor> LIST = new CopyOnWriteArrayList<>();
public static void registerAll(Collection<Interceptor> its) { LIST.addAll(its); }
public static void attachAll(OkHttpClient.Builder b){
for (Interceptor it : LIST) {
boolean exists = b.interceptors().stream()
.anyMatch(x -> x.getClass().getName().equals(it.getClass().getName()));
if (!exists) b.addInterceptor(it);
}
}
}
@Configuration
public class DemoAutoCollectConfig {
@Bean
public ApplicationRunner collectInterceptors(List<Interceptor> interceptors){
return args -> DemoInterceptorBridge.registerAll(interceptors);
}
}
这样,业务侧无需感知有哪些拦截器,统一通过"桥"完成补挂,真正做到可插拔。
5. 让拦截器在 OkHttp 中真正生效(Demo)
java
public final class DemoOkHttpFactory {
public static OkHttpClient newClient(){
OkHttpClient.Builder b = new OkHttpClient.Builder()
.connectTimeout(Duration.ofSeconds(5))
.readTimeout(Duration.ofSeconds(10));
// 统一补挂所有已注册的拦截器
DemoInterceptorBridge.attachAll(b);
return b.build();
}
}
生效路径
-
Spring 启动:扫描并注册所有
Interceptor
Bean → 交给DemoInterceptorBridge
; -
构建客户端:使用
DemoOkHttpFactory.newClient()
获取OkHttpClient
; -
发起请求:OkHttp 按拦截器顺序依次调用
intercept()
→ 命中条件的拦截器执行逻辑,否则放行; -
运行期:通过
DemoRuntimeSwitch
更新开关与参数,无重启生效。
6. 端到端流程示意

7. 最佳实践
-
直通优先:非命中条件一律放行,降低链路抖动;
-
独立配置:把"是否启用""目标地址""签名策略"都放到运行时配置中;
-
安全策略:签名/加密算法做成可替换的策略接口;敏感字段统一脱敏;
-
顺序管理:有依赖关系的拦截器(例如"鉴权 → 访问日志")要明确注册顺序;
-
测试清单:命中/未命中、配置缺失、远端超时/5xx、请求体不可重复读取等场景;
-
可观测:埋点 traceId、耗时、命中率、错误码分布,日志注意截断与采样。
8. Demo 模板
java
//自定义拦截器骨架(最小实现)
public class MyDemoInterceptor implements Interceptor {
@Override public Response intercept(Chain chain) throws IOException {
Request req = chain.request();
// 前置:校验/打点/改写(按需)
Response resp = chain.proceed(req);
// 后置:统计/日志(按需)
return resp;
}
}
//Spring 注入 + 统一补挂
@Configuration
public class MyDemoOkHttpConfig {
@Bean public Interceptor myDemoInterceptor(){ return new MyDemoInterceptor(); }
@Bean public OkHttpClient httpClient(){
OkHttpClient.Builder b = new OkHttpClient.Builder();
DemoInterceptorBridge.attachAll(b);
return b.build();
}
}
//运行时开关(示意)
public final class MySwitch {
private final AtomicBoolean on = new AtomicBoolean(false);
public boolean isOn(){ return on.get(); }
public void setOn(boolean v){ on.set(v); }
}