JDK动态代理和cglib动态代理

本文详细解析了JDK动态代理和CGLIB动态代理的实现方式及内部机制,对比两者在性能上的差异,深入探讨了Method.invoke()的反射调用逻辑和CGLIB的动态字节码生成过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、JDK动态代理

实现方式

JDK动态代理通过反射的方式实现代理。

只能对实现接口的类进行代理。

1、创建接口

public interface B {
    void foo(String name);
}

2、创建被代理类(需要实现该接口)

public class BImpl implements B {
    @Override
    public void foo(String name) {
        System.out.println("Hello, " + name);
    }
}

3、创建代理对象并进行测试

public class JdkProxy {
    public static void main(String[] args) {
        //被代理的对象
        BImpl b = new BImpl();
        //被代理类BImpl的Class
        Class bClass = b.getClass();
//      使用JDK动态代理创建代理对象proxyB
        B proxyB = (B) Proxy.newProxyInstance(bClass.getClassLoader(), bClass.getInterfaces(), new InvocationHandler() {
            /**
             * 拦截代理对象执行的方法:当proxyB调用foo()方法的时候进行拦截
             * @param proxy 代理对象:proxyB
             * @param method 代理对象执行的方法: foo(),实际上是接口的方法
             * @param args 方法的参数:name
             * @return 实际的返还值, 如果没有返还值则为null
             * @throws Throwable
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object invoke = method.invoke(b, args);
                return invoke;
            }
        });
        //代理对象执行方法
        proxyB.foo("张三");
    }
}

4、invoke()方法调试查看

 

JDK动态代理过程解析

随着JDK版本的不断更新,JDK动态代理的速度也越来越快。

1、底层使用java反射来执行方法导致速度变慢

1.1、查看生成的代理类

因为代理类是存储来JVM内存中的,没有写入到本地磁盘。所以我们需要在main方法中加入下面的代码来让其创建的时候保存到本地磁盘,然后在当前项目的com/sun/proxy文件夹下查看。

System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

生成的代理类的class文件:

使用super.h.invoke(this, m3, new Object[]{var1})来通过反射执行方法

package com.sun.proxy;
​
import com.gypsophila.reflect.jdk.B;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
​
public final class $Proxy0 extends Proxy implements B {
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
​
  public $Proxy0(InvocationHandler var1) throws  {
    super(var1);
  }
​
  public final boolean equals(Object var1) throws  {
    try {
      return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    } catch (RuntimeException | Error var3) {
      throw var3;
    } catch (Throwable var4) {
      throw new UndeclaredThrowableException(var4);
    }
  }
​
  public final void foo(String var1) throws  {
    try {
      super.h.invoke(this, m3, new Object[]{var1});
    } catch (RuntimeException | Error var3) {
      throw var3;
    } catch (Throwable var4) {
      throw new UndeclaredThrowableException(var4);
    }
  }
​
  public final String toString() throws  {
    try {
      return (String)super.h.invoke(this, m2, (Object[])null);
    } catch (RuntimeException | Error var2) {
      throw var2;
    } catch (Throwable var3) {
      throw new UndeclaredThrowableException(var3);
    }
  }
​
  public final int hashCode() throws  {
    try {
      return (Integer)super.h.invoke(this, m0, (Object[])null);
    } catch (RuntimeException | Error var2) {
      throw var2;
    } catch (Throwable var3) {
      throw new UndeclaredThrowableException(var3);
    }
  }
​
  static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
      m3 = Class.forName("com.gypsophila.reflect.jdk.B").getMethod("foo", Class.forName("java.lang.String"));
      m2 = Class.forName("java.lang.Object").getMethod("toString");
      m0 = Class.forName("java.lang.Object").getMethod("hashCode");
    } catch (NoSuchMethodException var2) {
      throw new NoSuchMethodError(var2.getMessage());
    } catch (ClassNotFoundException var3) {
      throw new NoClassDefFoundError(var3.getMessage());
    }
  }
}

1.2、探究反射慢的原因

运行时加入-XX:+TraceClassLoading参数查看类加载情况

1)、测试代码

public class TestClassLoad {
    public static void main(String[] args) throws Exception {
        Class<?> clz = Class.forName("com.gypsophila.reflect.cglib.B");
        Object o = clz.newInstance();
        Method m = clz.getMethod("foo", String.class);
        for (int i = 0; i < 16; i++) {
            m.invoke(o, Integer.toString(i));
        }
    }
}

2、源码

2.1、查看Method.invoke()方法的底层实现

 

可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。

2.2、MethodAccessor

两个实现类

 

1)、在第一次调用一个实际Java方法对应得Method对象的invoke()方法时使用newMethodAccessor()构造方法创建MethodAccessor。

public MethodAccessor newMethodAccessor(Method var1) {
    checkInitted();
    if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) {
      return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers());
    } else {
        //第一次实例化创建
      NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1);
      DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2);
      var2.setParent(var3);
        //最后返回的MethodAccessor为DelegatingMethodAccessorImpl
        /*
        实际的Java方法只有一个对应的Method对象作为root。这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时新创建Method对象把root包装起来再给用户。
        */
      return var3;
    }
  }

2)、DelegatingMethodAccessorImpl是一个间接层,方便在native与Java版的MethodAccessor之间实现切换。

class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
  private MethodAccessorImpl delegate;
​
  DelegatingMethodAccessorImpl(MethodAccessorImpl var1) {
    this.setDelegate(var1);
  }
//invoke()方法
  public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
    return this.delegate.invoke(var1, var2);
  }
​
  void setDelegate(MethodAccessorImpl var1) {
    this.delegate = var1;
  }
}

3)、在超过阈值(默认值是15)前每次调用NativeMethodAccessorImpl.invoke()方法被调用时,都会增加一个调用次数计数器一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版,在下一次调用的时候会使用Java版的MethodAccessor。

public Object invoke(Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException {
    if (++this.numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this.method.getDeclaringClass())) {
      MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this.method.getDeclaringClass(), this.method.getName(), this.method.getParameterTypes(), this.method.getReturnType(), this.method.getExceptionTypes(), this.method.getModifiers());
      this.parent.setDelegate(var3);
    }
​
    return invoke0(this.method, var1, var2);
  }

Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。

这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。

4)、本文开头的例子的B.foo(),生成的Java版MethodAccessor大致如下:

1)、使用RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();获取进程ID。

2)、进入JAVA_HOME的lib目录下,在命令行执行java -cp ./sa-jdi.jar sun.jvm.hotspot.HSDB就可唤起HSDB的图形界面,点击File-》Attach to Hotspot Process,输入进程ID,点击OK。

3)、代码

package sun.reflect;
​
import com.gypsophila.reflect.cglib.B;
import java.lang.reflect.InvocationTargetException;
​
public class GeneratedMethodAccessor1 extends MethodAccessorImpl {
  public Object invoke(Object var1, Object[] var2) throws InvocationTargetException {
    if (var1 == null) {
      throw new NullPointerException();
    } else {
      B var10000;
      String var10001;
      try {
          //获得B的实现类对象
        var10000 = (B)var1;
          //这里因为自动解析只有一个参数所以去掉了循环
        if (var2.length != 1) {
          throw new IllegalArgumentException();
        }
          //将参数拆箱
        var10001 = (String)var2[0];
      } catch (NullPointerException | ClassCastException var4) {
        throw new IllegalArgumentException(var4.toString());
      }
​
      try {
          //调用目标方法
        var10000.foo(var10001);
        return null;
      } catch (Throwable var3) {
        throw new InvocationTargetException(var3);
      }
    }
  }
​
  public GeneratedMethodAccessor1() {
  }
}

JDK动态代理慢的原因

1、Method.invoke()本身要用Object数组包装参数

2、每次调用都必须检查方法的可见性(在Method.invoke()里)

3、,也必须检查每个实际参数与形式参数的类型匹配性(在NativeMethodAccessorImpl.invoke0()里或者生成的Java版MethodAccessor.invoke()里)

4、编译器不会对反射代码进行调优

二、cglib动态代理

实现方式

采用继承代理类的方式进行代理。

类无论是实现接口还是没有实现接口都可以进行代理,但是不能对有final修饰的类或有final修饰的方法进行代理。

1、导入依赖

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

2、创建代理对象并进行测试

//用于创建代理对象
        Enhancer enhancer = new Enhancer();
        //设置被代理的父类
        enhancer.setSuperclass(B.class);
        //设置如何代理
        enhancer.setCallback(new MethodInterceptor() {
            /**
             *
             * @param o 增强对象:enhancedB
             * @param method 被拦截的方法
             * @param objects 方法的参数
             * @param methodProxy 方法签名的表示形式,包含方法名称,返回类型和参数类型。用于调用父类(非拦截方法);可以根据需要多次调用
             * @return 最终返还值
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                return methodProxy.invokeSuper(o,objects); 
            }
        });
//创建代理对象
        B enhancedB = (B) enhancer.create();
        enhancedB.foo("张良");

3、invoke()方法调试

MethodInterceptor会拦截增强对象执行的方法(在增强对象中进行了覆盖重写)。

为什么不用以下的方式?

method.invoke(o,objects);
methodProxy.invoke(o,objects);

因为最终执行的还是增强对象的方法,会被再次拦截,一直递归,最终导致栈溢出。

 

cglib动态代理过程解析

1、查看生成的代理类

在main方法中使用以下代码将被代理类保存在本地磁盘

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:/");

代理方式:

public class B​1ea38d09 extends B implements Factory {

public class B$$EnhancerByCGLIB$$1ea38d09 extends B implements Factory {
  private boolean CGLIB$BOUND;
  public static Object CGLIB$FACTORY_DATA;
  private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
  private static final Callback[] CGLIB$STATIC_CALLBACKS;
  private MethodInterceptor CGLIB$CALLBACK_0;
  private static Object CGLIB$CALLBACK_FILTER;
  private static final Method CGLIB$foo$0$Method;
  private static final MethodProxy CGLIB$foo$0$Proxy;
  private static final Object[] CGLIB$emptyArgs;
  private static final Method CGLIB$equals$1$Method;
  private static final MethodProxy CGLIB$equals$1$Proxy;
  private static final Method CGLIB$toString$2$Method;
  private static final MethodProxy CGLIB$toString$2$Proxy;
  private static final Method CGLIB$hashCode$3$Method;
  private static final MethodProxy CGLIB$hashCode$3$Proxy;
  private static final Method CGLIB$clone$4$Method;
  private static final MethodProxy CGLIB$clone$4$Proxy;
​
  static void CGLIB$STATICHOOK1() {
    CGLIB$THREAD_CALLBACKS = new ThreadLocal();
    CGLIB$emptyArgs = new Object[0];
    Class var0 = Class.forName("com.gypsophila.reflect.cglib.B$$EnhancerByCGLIB$$1ea38d09");
    Class var1;
    CGLIB$foo$0$Method = ReflectUtils.findMethods(new String[]{"foo", "(Ljava/lang/String;)V"}, (var1 = Class.forName("com.gypsophila.reflect.cglib.B")).getDeclaredMethods())[0];
    CGLIB$foo$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "foo", "CGLIB$foo$0");
    Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
    CGLIB$equals$1$Method = var10000[0];
    CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
    CGLIB$toString$2$Method = var10000[1];
    CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
    CGLIB$hashCode$3$Method = var10000[2];
    CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
    CGLIB$clone$4$Method = var10000[3];
    CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
  }
​
  final void CGLIB$foo$0(String var1) {
    super.foo(var1);
  }
​
  public final void foo(String var1) {
    MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
    if (var10000 == null) {
      CGLIB$BIND_CALLBACKS(this);
      var10000 = this.CGLIB$CALLBACK_0;
    }
    if (var10000 != null) {
      var10000.intercept(this, CGLIB$foo$0$Method, new Object[]{var1}, CGLIB$foo$0$Proxy);
    } else {
        
        //调用被代理对象的方法
      super.foo(var1);
    }
  }
  final boolean CGLIB$equals$1(Object var1) {
    return super.equals(var1);
  }
​
 
​
  public Callback[] getCallbacks() {
    CGLIB$BIND_CALLBACKS(this);
    return new Callback[]{this.CGLIB$CALLBACK_0};
  }
​
  public void setCallbacks(Callback[] var1) {
    this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
  }
​
  static {
    CGLIB$STATICHOOK1();
  }
}
资源下载链接为: https://pan.quark.cn/s/9648a1f24758 在当今信息化时代,管理系统已成为企业、组织乃至虚拟世界中不可或缺的工具。本文将深入探讨“地府后台管理系统”,解析其核心概念、功能以及可能涉及的技术栈,以期为读者提供全面的了解。需要明确的是,“地府后台管理系统”在现实生活中并不存在,但在虚构或游戏场景中,它可能是用于维护虚拟世界运行的后台工具。它通常负责角色管理、资源分配、事件触发、数据存储等后台任务,确保虚拟环境的正常运转。 管理系统的核心在于功能模块。对于“地府后台管理系统”,我们可以设想以下关键部分:一是角色管理,用于管理地府中的各类角色,如鬼魂、判官、牛头马面等,涵盖角色创建、权限分配及状态跟踪等功能;二是资源管理,负责虚拟资源(如魂魄、冥币等)的生成、分配与消耗,确保资源合理流动;三是事件调度,设定各类事件(如转世轮回、阳间报应等),实现定时或条件触发,推动虚拟世界发展;四是数据存储与查询,记录并存储所有操作产生的数据,数据库技术在此环节极为重要,可能涉及SQL或NoSQL数据库的选择;五是报表与分析,提供统计报表,分析地府运行情况,如魂魄流转效率、资源消耗趋势等;六是安全防护,保障系统安全,防止非法访问或数据泄露,可能需要防火墙、加密算法、访问控制等技术。 在技术实现方面,可能涉及以下技术栈:前端技术,利用HTML、CSS、JavaScript构建用户界面,借助React或Vue等框架提升开发效率;后端技术,采用Java、Python或Node.js作为服务器端语言,通过Spring Boot或Django等框架搭建后端服务;数据库,根据需求选择MySQL、PostgreSQL等关系型数据库或MongoDB等非关系型数据库;服务器架构,可能采用微服务架构,使系统更灵活、可扩展;API设计,遵循RESTful API标准实现前
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值