proxy拦截器返回的是代理对象,真正返回目标对象值的是invocationhandler中method.invoke(target, args)的调用结果;返回值由invoke方法决定,可原样返回、包装增强或提前返回;controller层统一包装应使用responsebodyadvice而非proxy。

Proxy 拦截器本身不直接“返回目标对象的值”,它返回的是一个代理对象(proxy),该对象在调用方法时,通过 InvocationHandler 的 invoke 方法触发逻辑,并最终委托给真实目标对象执行——真正返回“目标对象的值”的,是 invoke 中对 method.invoke(target, args) 的调用结果。
核心逻辑:invoke 方法决定返回值
在基于 JDK 动态代理的 InvocationHandler 实现中,所有方法调用都会被路由到 invoke 方法。是否返回目标对象的原始值、修改后值或完全自定义值,全由你在 invoke 中如何处理决定:
-
原样返回目标值:调用
method.invoke(target, args)并直接 return 其返回值 - 包装/增强返回值:获取原始返回值后,做日志、转换、加壳(如统一响应体)再返回
-
提前返回(跳过目标):满足条件时(如缓存命中、权限拒绝),不调用
method.invoke,直接 return 自定义对象
典型示例:返回并包装目标方法结果
以下是一个拦截器对 service 方法返回值进行统一包装的 InvocationHandler 示例:
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
public class ResponseWrapperHandler implements InvocationHandler {
private final Object target;
public ResponseWrapperHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 调用真实目标对象的方法,获取原始返回值
Object result = method.invoke(target, args);
// 2. 判断是否需要包装(例如非 void 方法、非特殊注解标记)
if (result != null && !method.getReturnType().equals(Void.TYPE)) {
// 3. 包装为统一响应格式,如 CommonResult.success(result)
return CommonResult.success(result);
}
return result;
}
}
注意:Proxy 与 Spring 拦截器的区别
不要混淆 JDK Proxy 和 Spring 的 HandlerInterceptor:
- JDK Proxy / CGLIB 是面向切面(AOP)底层机制,作用于 service 层方法调用,能真正拿到并修改目标方法的返回值
-
Spring MVC HandlerInterceptor 作用于 Controller 处理流程,
postHandle拿不到 JSON 响应体(只拿到ModelAndView,且前后端分离下基本为空),afterCompletion更无法修改已写出的响应;真正适合统一包装 Controller 返回值的是ResponseBodyAdvice
替代方案:Controller 层返回值统一处理
若目标是修改 Controller 的 HTTP 响应内容(如所有 @ResponseBody 方法都套一层 {code:0,data:x}),应使用:
-
@RestControllerAdvice + ResponseBodyAdvice<object></object>:在序列化前拦截并重写返回值 - 配合
@IgnoreResponseAdvice注解实现按需豁免 - 比 Proxy 更精准、更符合 Web 层语义,且无需手动创建代理










