核心是通过反射在不修改原代码前提下实现方法调用前后的逻辑增强。Java用Proxy+InvocationHandler基于接口代理,Go用reflect.Value手动构造调用链;二者均需解耦切面、保证参数透传,但受限于性能、类型安全与调试难度。

核心在于用反射把目标方法“包一层”——不改原代码,却能在调用前后插入日志、权限、监控等逻辑。Java 中最直接的方式是结合 Proxy 类与 InvocationHandler 接口,而 Go 则依赖 reflect 包手动封装调用链。两者思路一致,但实现机制不同。
Java:基于接口的 JDK 动态代理
这是最标准、无需第三方库的 AOP 拦截方式,前提是目标类必须实现接口。
- 定义一个接口(如
UserService)和其实现类(UserServiceImpl) - 编写
InvocationHandler实现类,在invoke()方法中统一处理前置(before)、目标执行、后置(after)及异常逻辑 - 调用
Proxy.newProxyInstance(),传入类加载器、目标接口数组、以及你的InvocationHandler实例,得到代理对象 - 后续所有对该接口的调用,都会经由
invoke()分发,自然完成拦截
Go:用 reflect.Value 手动构造调用链
Go 没有内置 Proxy 类,但可通过反射在运行时获取方法签名、拼装参数、调用并捕获返回值,从而模拟拦截。
⚠️ 已弃用 — 该技能已拆分为两个独立技能,以提升模块化程度:**webchat-https-proxy**(HTTPS/WSS 反向代理)和 **webchat-voi...**
- 接收任意结构体实例,用
reflect.ValueOf(obj)和reflect.TypeOf(obj)获取其类型与值 - 代理结构体中保存原始对象,并提供通用
Call(methodName string, args ...interface{})方法 - 在
Call内:先触发 before 切面;再用reflect.Value.MethodByName().Call()执行目标方法;最后触发 after 或 recover 异常 - 为保持接口兼容,通常要求目标对象实现某个统一接口(如
Invoker),代理对象也实现它
关键共性:拦截点必须可插拔
无论 Java 还是 Go,真正支撑 AOP 的不是反射本身,而是把“增强逻辑”从目标方法中解耦出来,通过统一入口注入。
- 切面(Aspect)应独立成类或函数,比如
LoggingAspect或func(ctx context.Context) error - 代理层只负责调度:决定哪些方法需要被拦截、按什么顺序执行切面(before → target → after → recover)
- 参数与返回值需完整透传,Java 中靠
Object[] args和Object返回;Go 中需用[]reflect.Value映射并还原
注意绕不开的限制
这类纯反射方案不是银弹,实际落地时得心里有数:
- Java 代理只能作用于接口,无法代理具体类(除非用 CGLIB,但那是字节码增强)
- Go 反射调用比直接调用慢 5–10 倍,不适合高频核心路径;建议仅用于管理后台、配置类或低频服务入口
- 两者都丢失编译期类型检查:方法名写错、参数类型不匹配,要到运行时才 panic 或抛异常
- 调试困难——堆栈里会多出 proxy/invoke 层,IDE 也无法跳转到真实方法










