Java两种代理模式详解

如有错误欢迎指出

是jdk动态代理是从一步步开始写完

特性/维度 JDK 动态代理 CGLIB 动态代理
是否需要接口 ✅ 需要接口 ❌ 不需要接口(可代理普通类)
代理原理 基于反射实现接口方法的代理 继承目标类并重写方法,基于 ASM 字节码操作
代理类结构 生成实现目标接口的代理类 生成子类(目标类的子类)
性能(启动时) 创建代理类较快 创建代理类稍慢(涉及字节码操作)
性能(执行时) 方法调用稍慢(反射) 方法调用较快(直接调用子类方法)
适合场景 有接口的服务类,如 Service 接口层 没有接口的类,或对性能要求较高的场景
常用框架支持 Spring AOP 默认使用 JDK Proxy(有接口时) Spring AOP 在无接口时使用 CGLIB
类或方法限制 接口方法都能代理 final 类或 final 方法不能被代理
生成方式 Proxy.newProxyInstance() Enhancer.create()(来自 CGLIB)
配置示例(Spring) <aop:aspectj-autoproxy/>(默认) 配合 <aop:aspectj-autoproxy proxy-target-class="true"/>

https://www.doubao.com/thread/w25860ff971baf089

java proxy增强

java 复制代码
public class JdkProxyDemo {

    interface Foo {
        void foo();
        int bar();
    }

    static final class Target implements Foo {
        public void foo() {
            System.out.println("target foo");
        }
        public int bar() {
            System.out.println("target bar");
            return 100;
        }
    }

    // jdk 只能针对接口代理
    // cglib
    public static void main(String[] param) throws IOException {
        // 目标对象
        Target target = new Target();
    ///获取类加载器
        ClassLoader loader = JdkProxyDemo.class.getClassLoader(); 
    // 创建代理对象
        Foo proxy = (Foo) Proxy.newProxyInstance(
            loader,                        // 类加载器
            new Class[]{Foo.class},        // 代理类需要实现的接口
            new InvocationHandler() {      // 方法调用处理器
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("before..."); // 方法调用前的增强逻辑
                    Object result = method.invoke(target, args); // 调用目标对象的方法
                    System.out.println("after....");  // 方法调用后的增强逻辑
                    return result; // 返回目标方法的执行结果
                }
            }
        );
        proxy.foo();
        proxy.bar();

    }
}
复制代码
before...
target foo
after....
before...
target bar
after....
java 复制代码
   public static Object newProxyInstance(ClassLoader loader,//加载器
                                          Class<?>[] interfaces, ///实现的接口
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
   }
java 复制代码
public interface InvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args) //代理逻辑
        throws Throwable;
}

java cglib Enhancer代理增强

继承父类实现

java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxyDemo {

    static class Target {
        public void foo() {
            System.out.println("target foo too");
        }
    }

    public static void main(String[] args) {
        // 使用 CGLIB 创建代理对象
        Target proxy = (Target) Enhancer.create(Target.class, new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
                
                // 方式 1:反射调用目标对象方法(性能较差)
                // Object result = method.invoke(new Target(), args);

                // 方式 2:使用 methodProxy.invokeSuper() 调用父类方法(CGLIB 方式,性能更好)
                Object result = methodProxy.invokeSuper(proxy, args);

                System.out.println("after...");
                return result;
            }
        });

        // 调用代理方法
        proxy.foo();
    }
}

jdk 反射源码探究

第一个版本

代理类

java 复制代码
public class A01proxy {
    interface Foo{
        void foo();

    }
   public static class Target implements Foo{
@Override
        public void foo()
        {
            System.out.println("Target.foo()");
        }

   }
    public static void main(String[] args)
    {
        Foo Proxy = new Proxyq();
        Proxy.foo();
    }

}
java 复制代码
public class Proxyq implements A01proxy.Foo {

    @Override
    public void foo() {

        System.out.println("foo");
        new Target().foo();

    }
}
java 复制代码
foo
Target.foo()
Target.foo()]()

//类似于什么设计模式呢

缺点-代理的方法直接写死到内部,如果要新增很麻烦

版本2

优化点解耦 -让代理的方法交给外部来写

新增接口-使得逻辑再外部使用时实现

接口

java 复制代码
public interface InvocationHandler {
    void invoke();
}
java 复制代码
public class Proxyq implements A01proxy.Foo {
    private InvocationHandler h;

    public Proxyq(com.itheima.a13.a02.InvocationHandler foo) {
        this.h = foo;
    }
    @Override
    public void foo() {
        h.invoke();
    }
}
java 复制代码
public class A01proxy {
    interface Foo{
        void foo();
    }
   public static class Target implements Foo{
@Override
        public void foo()
        {
            System.out.println("Target.foo()");
        }
   }
    public static void main(String[] args)
    {
        //外部实现
        Foo Proxy = new Proxyq(new InvocationHandler(){
            @Override
            public void invoke() {
                System.out.println("foo");
                new Target().foo();
            }
        });
        Proxy.foo();
    }
}

缺点

如果是boo方法想代理呢,那么就会出现错误。(Method method)需要传递调用的方法来动态调用

Proxy.boo()不知道怎么处理。

三 优化方法过多无法处理

java 复制代码
public interface InvocationHandler {
    void invoke(Method  method, Object[] args) throws InvocationTargetException, IllegalAccessException;
}

我们从源码中看到就是这样,我们来实现一下

java 复制代码
public class A01proxy {
    interface Foo{
        void foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
        void bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException;
    }
   public static class Target implements Foo{
@Override
        public void foo()
        {
            System.out.println("Target.foo()");
        }
       @Override
       public void bar() {
            System.out.println("Target.bar()");
       }
   }
    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        Foo Proxy = new Proxyq(new InvocationHandler(){
            @Override
            public void invoke(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
                System.out.println("before");
                method.invoke(new Target(), args); ///反射
            }
        });
        Proxy.foo();
        Proxy.bar();
    }
}
java 复制代码
public interface InvocationHandler {
    void invoke(Method  method, Object[] args) throws InvocationTargetException, IllegalAccessException;
}
java 复制代码
public class Proxyq implements A01proxy.Foo {
    private InvocationHandler h;

    public Proxyq(InvocationHandler foo) {
        this.h = t;
    }

    @Override
    public void foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        System.out.println("Proxyq.foo()");
        Method foo= A01proxy.Foo.class.getMethod("foo");//反射
         h.invoke( foo,new Object[0]);//执行
    }

    @Override
    public void bar() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        System.out.println("Proxyq.bar()");
        Method bar= A01proxy.Foo.class.getMethod("bar");
        h.invoke(bar,new Object[0]);

    }
}

反射拿去了方法

缺点,拿不到返回值

优化 3.1返回值处理

java 复制代码
 public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        Foo Proxy = new Proxyq(new InvocationHandler(){
            @Override
            public Object invoke(Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {//返回值处理
                System.out.println("before");
              return   method.invoke(new Target(), args);

            }
        });
        Proxy.foo();
        Proxy.bar();
    }
java 复制代码
public interface InvocationHandler {
    Object invoke(Method  method, Object[] args) throws InvocationTargetException, IllegalAccessException;
}
java 复制代码
    @Override
    public void foo() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        System.out.println("Proxyq.foo()");
        Method foo= A01proxy.Foo.class.getMethod("foo");
         Object result=  h.invoke( foo,new Object[0]);
        //这里需要return,但是我没有处理
    }

优化 保存与jdk一致

新增参数object->吧代理类传到实现里面去了

异常处理

注意异常顺序

代码优化

///防止多次反射拿去method

最终

java 复制代码
public class $Proxy0  implements test01.Foo { //代理类
    test01.InvocationHandler h;
    public $Proxy0(test01.InvocationHandler h)
    {
        this.h = h;
    }
    static Method foo;
    static Method bar;
    static {
        try {
            foo = test01.Foo.class.getMethod("foo");
            bar = test01.Foo.class.getMethod("bar");
        } catch (NoSuchMethodException e) {
            throw new NoSuchMethodError(e.getMessage());
        }
    }


//代理方法

    @Override
    public void foo() {
        try {
            h.invoke(this, foo, new Object[0]);
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }

    @Override
    public int bar() {
        try {
            Object result = h.invoke(this, bar, new Object[0]);
            return (int) result;
        } catch (RuntimeException | Error e) {
            throw e;
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
}
java 复制代码
public class test01 {

    interface Foo {
        void foo();

        int bar();
    }

    static class Target implements Foo {
        public void foo() {
            System.out.println("target foo");
        }

        @Override
        public int bar() {
            System.out.println("target bar");
            return 100;
        }
    }

    interface InvocationHandler {
        Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    }

    public static void main(String[] param) {
        Foo proxy = new $Proxy0(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 1. 功能增强
                System.out.println("before...");
                //2.调用方法
                Object result = method.invoke(new Target(), args);
                return result;
            }

        });
        proxy.foo();
        proxy.bar();


    }
}

真实源码

由编译器生成的吗,还是java语法特性

JVM 在运行期生成 $Proxy

运行期

java 复制代码
public class A12 {

    interface Foo {
        void foo();
        int bar();
    }

    static class Target implements Foo {
        public void foo() {
            System.out.println("target foo");
        }

        @Override
        public int bar() {
            System.out.println("target bar");
            return 100;
        }
    }

    /*interface InvocationHandler {
        Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    }*/

    public static void main(String[] param) throws IOException {
        Foo proxy = new $Proxy0(new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
                // 1. 功能增强
                System.out.println("before...");
                // 2. 调用目标
//                new Target().foo();
                return method.invoke(new Target(), args);
            }
        });
        System.out.println(proxy.getClass().getName());
        proxy.foo();
        proxy.bar();
        System.in.read();
        /*
            学到了什么: 代理一点都不难, 无非就是利用了多态、反射的知识
                1. 方法重写可以增强逻辑, 只不过这【增强逻辑】千变万化, 不能写死在代理内部
                2. 通过接口回调将【增强逻辑】置于代理类之外
                3. 配合接口方法反射(也是多态), 就可以再联动调用目标方法
         */
    }
}

jdk代理字节码生成

asm技术-动态生成字节码文件

jdk反射优化

反射调用次数过程

先用 Native 实现应对低频调用。

检测到高频调用后再生成字节码类(GeneratedMethodAccessor),让反射调用性能接近普通方法调用。

  • 访问器类型 表示 JVM 在不同阶段使用的 MethodAccessor 实现。
  • 红圈标的 TestMethodInvoke.foo((int)c))说明 JVM 已经把反射"编译"成了直接调用。
  • 这种生成类只在高频调用的情况下才会出现,目的是避免一开始就生成大量类浪费内存。

cglib原理

介绍

CGLIB(Code Generation Library)是一个基于 ASM 的字节码生成库,用来在运行期生成类。

它可以在运行期为某个类创建一个子类代理,从而实现方法拦截(AOP)。

JDK 动态代理只能代理 接口

Spring 的很多 bean 没有实现接口,所以需要使用:

  • CGLIB 生成一个子类去做代理。

例如:

java 复制代码
class UserService { ... }
↓
class UserService$$EnhancerByCGLIB extends UserService { ... }

然后把你原来对象的功能"增强"。

CGLIB 的核心构成只有两点:

继承目标类生成子类

CGLIB 通过 ASM 动态生成一个子类

子类中重写目标类的非 final 方法

在重写方法中调用拦截器 MethodInterceptor

MethodInterceptor.intercept() 做方法增强

代理类方法 → MethodInterceptor.intercept()

你可以在 intercept 里:

  • 加日志
  • 加权限校验
  • 加事务
  • 加耗时统计

然后决定是否:

  • 调用父类原方法(proxy.invokeSuper)
  • 不调用原方法(拦截)
  • 修改参数
  • 修改返回值

CGLIB 的调用链(非常关键)

假设目标类:

java 复制代码
class Service {
    void save() { ... }
}

CGLIB 会生成:

java 复制代码
class Service$$EnhancerByCGLIB extends Service {
    @Override
    void save() {
        // 调用拦截器
        interceptor.intercept(this, saveMethod, args, methodProxy);
    }
}
java 复制代码
serviceProxy.save()
    ↓
intercept(proxyObj, method, args, methodProxy)
    ↓
methodProxy.invokeSuper(proxyObj, args)
    ↓
父类 Service.save()

MethodProxy 的作用(面试常问)

java 复制代码
CGLIB 为每个方法创建一个 MethodProxy,它负责:

绑定代理类方法与父类方法的映射

在调用父类方法时使用 FastClass(避免反射)

提供 invokeSuper 方法跳过代理逻辑
java 复制代码
method.invoke() → 反射 → 慢
methodProxy.invokeSuper() → FastClass → 快

FastClass 原理(提高性能的关键)

CGLIB 会为类生成一个 "FastClass",把方法调用变成整数索引:

java 复制代码
methodIndex = 3
switch(3):
    case 3: return obj.save()

避免使用反射,性能大幅提升。

JDK 动态代理无法达到这个性能。

java 复制代码
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Target.class);
enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) 
            throws Throwable {

        System.out.println("before...");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("after...");

        return result;
    }
});

Target proxy = (Target) enhancer.create();
proxy.save();

CGLIB = 运行期生成子类 + 重写方法 + 拦截调用 + FastClass 加速

代码

有字段:没问题,CGLIB 会继承它们。

构造函数不执行:字段初始化可能不完整。

final/private/static 方法无法被增强。

如果字段依赖构造注入,可能出现 null。

继承方法

步骤 操作 代码示例 说明
1 获取 Class 对象 Class<?> clazz = Target.class; 反射的入口,拿到类的元数据。也可以用 Class.forName("包名.Target")
2 获取 Method 对象 Method m = clazz.getMethod("save"); 通过方法名和参数类型,获取 public 方法。若方法是私有的或非 public,用 getDeclaredMethod
3 创建目标对象实例 Target obj = new Target(); 如果要调用实例方法,需要先有对象。静态方法可以直接传 null 调用。
4 调用方法 m.invoke(obj); invoke 第一个参数是目标对象,后面是方法参数。
5 处理异常 NoSuchMethodExceptionIllegalAccessExceptionInvocationTargetException 反射涉及多种检查型异常,需要捕获或抛出。

初始化了 反射拿取了方法-同时创建了一个能够在运行期执行"原始方法/父类方法" 的快速调用器(invocation helper)

java 复制代码
save0Proxy = MethodProxy.create(
    Target.class,       // 被代理的类
    Proxy.class,        // 代理类(由 CGLIB 生成的子类)
    "()V",              // 方法签名(这里是无参、void 返回值)
    "save",             // 原方法名
    "saveSuper"         // 父类方法名(或调用原始实现的方法名)
);
步骤 反射 (Method.invoke) 做法 CGLIB (MethodProxy.invoke) 做法 速度差异原因
1. 定位方法 运行时查找 Method 对象(Class.getMethod MethodProxy.create(...) 时直接生成代理类字节码,方法引用是编译进字节码的 反射是运行时查找,CGLIB 是类加载时就确定好
2. 访问权限检查 每次调用都会做 Method 的访问修饰符检查(JVM 安全检查) CGLIB 生成的子类直接调用 super.save(),不触发反射的安全检查 避免了重复安全检查
3. 参数传递 invoke 内部用 Object 数组拆装参数(装箱/拆箱) CGLIB 生成的方法直接用原始参数类型传递(例如直接 void save() 调用) 避免了装箱拆箱和类型转换
4. 方法调用 JVM 通过反射调用 MethodAccessorinvoke 方法,间接层多 CGLIB 代理类直接发起 super.save() 的字节码调用 少了中间调用层,直接走 invokevirtual 或 invokespecial
5. 调用父类实现 需要反射再找父类的 Method 已在 create 时绑定 "saveSuper" 方法,生成字节码直接调用 直接调用字节码方法引用
最终效果 每次调用都走反射 API,动态解析 调用时和普通方法几乎一样快(接近直接调用速度) 大幅减少运行期开销

create(...) 这一步,CGLIB 就会用 ASM 动态生成一个 Proxy.class 子类,并且在字节码里写死类似:

java 复制代码
public void save() {
    // 先执行增强逻辑...
    super.save(); // 直接字节码调用
}

这样后续 invoke 调用的时候,就直接跳到字节码方法 ,完全绕过了 Method.invoke 的反射路径。

后续只需调用即可

java 复制代码
Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    // 增强逻辑前置,如日志、权限校验
    System.out.println("方法调用前增强");

    // 通过 proxy 调用父类原始方法
    Object result = proxy.invokeSuper(obj, args);

    // 增强逻辑后置,如事务提交、日志记录
    System.out.println("方法调用后增强");

    return result;
}

// CGLIB 动态生成的代理类里,invokeSuper 实际上是跳过代理直接调用父类方法
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    // obj 是代理对象,调用父类(Target)的 save 方法,传入参数 args
    // 这里调用的是类似 JVM 指令 invokespecial,直接调用父类实现,避免代理递归
    return invokeParentMethod(obj, "save", args);
}
  1. intercept 是代理切入点
  2. invokeSuper 是跳过代理类,直接调用父类方法的快速路径
  3. CGLIB 使用 FastClass 避免反射,提高性能
  4. invokeSuper 用 invokespecial 指令调用父类实现,防止递归

走两个 一个反射调用 一个代理类调用

java 复制代码
before...
save()
before...
save(int)
before...
save(long)

cglib 无反射原理

代理类调用避免反射

重写该fastclass.class- invoke 方法

fastclass

java 复制代码
public class TargetFastClass {
//创建方法签名

    static Signature s0 = new Signature("save", "()V");
    static Signature s1 = new Signature("save", "(I)V");
    static Signature s2 = new Signature("save", "(J)V");

 // 获取目标方法的编号
    /*
        Target
            save()              0
            save(int)           1
            save(long)          2
        signature 包括方法名字、参数返回值
     */

public int getIndex(Signature signature) {
        if (s0.equals(signature)) {
            return 0;
        } else if (s1.equals(signature)) {
            return 1;
        } else if (s2.equals(signature)) {
            return 2;
        }
        return -1;
    }
   // 根据方法编号, 正常调用目标对象方法
    public Object invoke(int index, Object target, Object[] args) {
        if (index == 0) {
            ((Target) target).save();
            return null;
        } else if (index == 1) {
            ((Target) target).save((int) args[0]);
            return null;
        } else if (index == 2) {
            ((Target) target).save((long) args[0]);
            return null;
        } else {
            throw new RuntimeException("无此方法");
        }
    }

    public static void main(String[] args) {
        TargetFastClass fastClass = new TargetFastClass();
        int index = fastClass.getIndex(new Signature("save", "(I)V"));
        System.out.println(index);
        fastClass.invoke(index, new Target(), new Object[]{100});
    }
}

代理方法 继承了原始类

java 复制代码
public class ProxyFastClass {
    static Signature s0 = new Signature("saveSuper", "()V");
    static Signature s1 = new Signature("saveSuper", "(I)V");
    static Signature s2 = new Signature("saveSuper", "(J)V");

    // 获取代理方法的编号
    /*
        Proxy
            saveSuper()              0
            saveSuper(int)           1
            saveSuper(long)          2
        signature 包括方法名字、参数返回值
     */
    public int getIndex(Signature signature) {
        if (s0.equals(signature)) {
            return 0;
        } else if (s1.equals(signature)) {
            return 1;
        } else if (s2.equals(signature)) {
            return 2;
        }
        return -1;
    }

    // 根据方法编号, 正常调用目标对象方法
    public Object invokeSuper(int index, Object proxy, Object[] args) {
        if (index == 0) {
            ((Proxy) proxy).saveSuper();
            return null;
        } else if (index == 1) {
            ((Proxy) proxy).saveSuper((int) args[0]);
            return null;
        } else if (index == 2) {
            ((Proxy) proxy).saveSuper((long) args[0]);
            return null;
        } else {
            throw new RuntimeException("无此方法");
        }
    }

    public static void main(String[] args) {
        ProxyFastClass fastClass = new ProxyFastClass();
        int index = fastClass.getIndex(new Signature("saveSuper", "()V"));
        System.out.println(index);

        fastClass.invokeSuper(index, new Proxy(), new Object[0]);
    }
}

区别

一个调用16次后进行一个方法一个代理的避免反射

一个就是两个代理类,避免反射 -代理类中的invoke是调用了原始方法-继承写的

相关推荐
要站在顶端5 小时前
Jenkins PR编号提取&环境变量赋值问题总结
java·servlet·jenkins
愚公移码5 小时前
蓝凌EKP产品:Hibernate 中 SessionFactory、Session 与事务的关系
java·数据库·hibernate·蓝凌
小此方5 小时前
Re:从零开始学C++(二)基础精讲·中篇:引用
开发语言·c++·底层
TT哇5 小时前
【每日八股】面经常考
java·面试
何中应5 小时前
【面试题-4】JVM
java·jvm·后端·面试题
老毛肚5 小时前
黑马头条-再回首
java
专注于大数据技术栈5 小时前
java学习--8个包装类
java·学习
Lyinj5 小时前
从一个编辑校验问题谈接口设计的边界
java·spring boot·python·学习
消失的旧时光-19435 小时前
Java 线程通信:彻底理解 wait / notify(原理 + 图解 + 实战)
java·开发语言