APS 系列文章 01

一行代理告别反射:Java 动态代理新选择 APS

写 Java 久了,几乎人人都碰过这类需求:给某个方法加日志、做鉴权、统计耗时、织入事务切面。这时候「动态代理」是最顺手的工具,但 JDK 生态里一直摆着一个尴尬的两难:

  • java.lang.reflect.Proxy 只能代理接口。你的业务对象一旦是具体 class,要么先抽接口,要么放弃。
  • CGLib 能代理类,但基于 FastClass/MethodProxy 那一套,性能一般,还有 ClassLoader 泄漏的历史包袱。

更关键的是,无论选哪边,拦截器里想调用「原来的方法」都绕不开反射:

java 复制代码
// JDK 动态代理:Method.invoke 是反射调用
Object result = method.invoke(target, args);

反射慢在哪?Method.invoke 每次调用都要做访问检查、参数装箱,而且它是一个 JVM 无法内联的边界------这意味着本来能被 JIT 优化成几条机器指令的热点方法,一旦套上代理就变成了一次反射派发。

APS(Accelerated Proxy Solution) 想解决的就是这件事:它在运行时用 ASM 直接生成代理类的字节码 ,拦截器里调用原方法用的是一条 INVOKESPECIAL 指令直接调用父类方法------没有反射、没有 MethodHandle、能被 JIT 完全内联。

直接说结果:类代理比 CGLib 快约 3--5 倍接口代理和 java.lang.reflect.Proxy 打平、default 方法快约 6 倍

一个 API,类和接口通吃

APS 只有一个入口 AcceleratedProxy.proxy(...),无论目标是类还是接口:

java 复制代码
// 类代理:拦截 Greeter 的每个方法
Greeter proxy = AcceleratedProxy.proxy(Greeter.class,
        (obj, method, args) -> {
            System.out.println("before " + method.getName());
            Object result = AcceleratedProxy.invokeSuper(obj, method, args);
            System.out.println("after " + method.getName());
            return result;
        });

// 接口代理:Calculator 连实现类都没有,运行时直接给行为
Calculator calc = AcceleratedProxy.proxy(Calculator.class,
        (obj, method, args) -> (int) args[0] + (int) args[1]);

注意两处细节,它们是 APS 设计里最关键的约定:

  1. Interceptor 是一个函数式接口 ,签名只有一个方法 Object intercept(Object proxy, Method method, Object[] args),所以可以直接用 lambda。
  2. 调用原方法用 AcceleratedProxy.invokeSuper(proxy, method, args) ,而不是 method.invoke(...)。这一行在生成的字节码里就是一条直接调用父类方法的指令,是整个性能优势的来源。

完整示例

下面这段代码完整可跑(源码在 examples/src/main/java/io/github/lamspace/blog/ch01/):

java 复制代码
public class Greeter {
    public String hello(String name) { return "Hello, " + name; }
    public String getGreeting() { return "Hi there"; }
}
java 复制代码
public interface Calculator {
    int add(int a, int b);
}
java 复制代码
import io.github.lamspace.AcceleratedProxy;

public class Main {
    public static void main(String[] args) {
        // 1. 类代理:拦截 Greeter 的每个方法
        Greeter greeter = AcceleratedProxy.proxy(Greeter.class,
                (proxy, method, mArgs) -> {
                    System.out.println("before " + method.getName());
                    Object result = AcceleratedProxy.invokeSuper(proxy, method, mArgs);
                    System.out.println("after " + method.getName());
                    return result;
                });

        System.out.println(greeter.hello("World"));

        // 2. 接口代理:Calculator 没有实现类,运行时给行为
        Calculator calc = AcceleratedProxy.proxy(Calculator.class,
                (proxy, method, mArgs) -> {
                    System.out.println("calling " + method.getName());
                    return (int) mArgs[0] + (int) mArgs[1];
                });

        System.out.println(calc.add(10, 20));
    }
}

运行输出:

text 复制代码
before hello
after hello
Hello, World
---
calling add
30

APS 到底提供了什么

除了「快」,APS 还补齐了一堆原生方案没有的能力,这些是它区别于 CGLib / JDK Proxy 的地方:

能力 APS CGLib JDK Proxy
代理具体类
代理接口
调用原方法的机制 直接 INVOKESPECIAL MethodProxy + FastClass Method.invoke 反射
GC 安全(隐藏类)
选择性拦截(方法分组) Group CallbackFilter
多接口代理
构造器拦截
静态方法代理
热重载 / 热替换
注解驱动 API

这张表里每一行,本系列后续都会拆开讲。

环境与安装

APS 要求 Java 25+ ,编译期依赖 ASM 9.7.1。目前还没上 Maven Central,先 git clone 下来 mvn install 到本地仓库即可:

bash 复制代码
git clone https://github.com/LamSpace/aps.git
cd aps
mvn install -DskipTests

小结与预告

这一篇只做了一件事:让你用几行代码跑起来第一个 APS 代理,并记住一个核心约定------invokeSuper 而不是反射

下一篇我们看它的第一个杀手级特性:方法分组 Group ------如何只拦截你关心的方法,而让其余方法以零开销直通父类。

本文示例代码见 aps-blog/examples,框架源码与完整文档见 APS 仓库

相关推荐
萧瑟余晖7 小时前
Java深入解析篇五十四之对象模型详解
java·开发语言
张文是假的啊7 小时前
Java | record | Controller逻辑
java·开发语言
勿忘,瞬间7 小时前
Mybatis高阶
java·数据库·mybatis
嘻哈baby9 小时前
Go 函数中的参数为什么不支持默认值?
java·开发语言·jvm
慧都小项9 小时前
MyEclipse 2026:当 Agent、MCP 与 Java 26 进入 Eclipse 工作流
java·eclipse·springboot·copilot·myeclipse
CoderYanger9 小时前
前端基础——JavaScript(基础语法)(下篇)
java·开发语言·前端·javascript·程序人生·面试·职场和发展
京师20万禁军教头10 小时前
39面向对象(高级)-设计模式
java·开发语言·设计模式
白山编程大哥10 小时前
Java 迭代器接口详解:从 Iterator 到 ListIterator 的完整指南
java·开发语言·windows
热心网友俣先生10 小时前
2026国赛C题:五年命题规律与预测
java·c语言·前端·数学建模
见叶之秋10 小时前
【C++】String类的使用(含模拟实现)
java·开发语言·c++