Java 静态代理动态代理
代理的功能也就如这个模型一样,对销售这个流程多了些处理:原先供应商直接出售给客户的商品,由代理商宣传销售。
代理在 Java 中,一般是对方法做出一些修改,具体要看应用场景,如:
- 抽离多个方法中重复代码(Spring AOP 就是这样)
- 过滤器, 拦截器
- 对其他包中对象的方法进行修改/增强
三种常见实现:自己写代理类(静态代理)、JDK Proxy(接口动态代理)、CGLIB(子类动态代理)。
静态代理
需要代理类实现被代理类的接口。
被代理类和接口
1public interface ISell {
2 void sell(double money);
3}
4
5class Seller implements ISell {
6 public void sell(double money) {
7 System.out.println("厂商得到:" + money);
8 }
9}
静态代理类实现
1class StaticProxySeller implements ISell {
2 private Seller superSeller;
3
4 public StaticProxySeller(Seller seller) {
5 this.superSeller = seller;
6 }
7
8 public void sell(double money) {
9 double profit = money * 0.2;
10 System.out.println("代理销售利润:" + profit);
11 superSeller.sell(money - profit);
12 }
13}
优点是直观,编译期就能看到代理类。缺点是每多一个接口、每多一个方法,代理类都要手写一遍,业务一大就炸。
JDK 动态代理
使用 Proxy.newProxyInstance() 在运行时生成代理对象,对接口方法做增强。
三个参数:
- 被代理类的 ClassLoader
- 被代理类实现的接口数组(必须有接口)
InvocationHandler,真正写增强逻辑的地方

匿名内部类实现
1Seller superSeller = new Seller();
2ISell proxySeller = (ISell) Proxy.newProxyInstance(
3 superSeller.getClass().getClassLoader(),
4 superSeller.getClass().getInterfaces(),
5 new InvocationHandler() {
6 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
7 if (method.getName().equals("sell")) {
8 Double money = (Double) args[0];
9 double profit = money * 0.2;
10 System.out.println("代理销售利润:" + profit);
11 return method.invoke(superSeller, money - profit);
12 }
13 return method.invoke(superSeller, args);
14 }
15 });
16proxySeller.sell(100);
invoke 的返回值要回传给调用方,不要把目标对象本身当返回值。
外部 Handler 实现
1class Handler implements InvocationHandler {
2 private Seller superSeller;
3
4 public Handler(Seller seller){
5 this.superSeller = seller;
6 }
7
8 /**
9 * @param proxy 生成的代理实例,不要在 invoke 里再调它的业务方法,容易递归
10 * @param method 被调用的方法
11 * @param args 方法参数
12 */
13 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
14 if ("sell".equals(method.getName())) {
15 Double money = (Double) args[0];
16 double profit = money * 0.2;
17 System.out.println("代理销售利润:" + profit);
18 return method.invoke(superSeller, money - profit);
19 }
20 return method.invoke(superSeller, args);
21 }
22}
创建代理对象
1Seller seller = new Seller();
2Handler handler = new Handler(seller);
3ISell proxySeller = (ISell) Proxy.newProxyInstance(
4 seller.getClass().getClassLoader(),
5 seller.getClass().getInterfaces(),
6 handler);
7proxySeller.sell(100);
动态代理方法执行流程

JDK 动态代理的限制:目标类必须实现接口。生成的是接口的实现类,不是目标类的子类。java.lang.reflect.Proxy 在运行时写出字节码,默认会缓存同类接口的代理 Class。
CGLIB 动态代理
目标类没有接口,或者你想直接代理具体类时,用 CGLIB。
它在运行时生成目标类的子类,再覆盖父类方法做增强。底层靠 ASM 改字节码。因此:
- 目标类不能是
final,被代理方法也不能是final/static/private(子类覆盖不了) - 会走子类构造,目标类最好留一个可见的无参构造
- Spring AOP 对没有接口的 Bean 默认就是这条路(Spring 5 起也可以全程用 CGLIB)
Maven 依赖(Spring 项目一般已经间接引入):
1<dependency>
2 <groupId>cglib</groupId>
3 <artifactId>cglib</artifactId>
4 <version>3.3.0</version>
5</dependency>
没有接口的目标类:
1public class Seller {
2 public void sell(double money) {
3 System.out.println("厂商得到:" + money);
4 }
5}
用 Enhancer + MethodInterceptor:
1import net.sf.cglib.proxy.Enhancer;
2import net.sf.cglib.proxy.MethodInterceptor;
3import net.sf.cglib.proxy.MethodProxy;
4
5import java.lang.reflect.Method;
6
7public class CglibSellInterceptor implements MethodInterceptor {
8 @Override
9 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
10 if ("sell".equals(method.getName())) {
11 Double money = (Double) args[0];
12 double profit = money * 0.2;
13 System.out.println("代理销售利润:" + profit);
14 args[0] = money - profit;
15 }
16 // 调父类方法,不要用 method.invoke(obj, args),会再次进 intercept,造成递归
17 return proxy.invokeSuper(obj, args);
18 }
19}
20
21Seller proxySeller = (Seller) Enhancer.create(Seller.class, new CglibSellInterceptor());
22proxySeller.sell(100);
Enhancer.create 等价于先 setSuperclass、再 setCallback、再 create。MethodProxy.invokeSuper 走的是生成子类里对 super.sell() 的调用。
三种代理怎么选
| 静态代理 | JDK 动态代理 | CGLIB | |
|---|---|---|---|
| 生成时机 | 编译期手写 | 运行时生成接口实现 | 运行时生成子类 |
| 目标类要求 | 通常也基于接口 | 必须有接口 | 具体类,非 final |
| 性能 | 直接调用,无反射 | 调用走 InvocationHandler |
多数场景比 JDK Proxy 快一些 |
| 适用 | 接口少、逻辑固定 | MyBatis Mapper、Spring 对接口 Bean 的 AOP | 没有接口的类、Spring 对类代理 |
Spring AOP、事务、日志切面,本质上都是这套「在调用前后插入逻辑」的代理。先搞清楚静态代理,再看 JDK / CGLIB,就不会觉得 AOP 是魔法。