返回顶部
首页 > 资讯 > 后端开发 > Python >Java cglib动态代理原理分析
  • 723
分享到

Java cglib动态代理原理分析

2024-04-02 19:04:59 723人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录一、cglib 动态代理示例  二、代理类分析 三、Fastclass 机制分析 本文分下面三个部分来分析cglib动态代理的原理。 cglib&n

本文分下面三个部分来分析cglib动态代理的原理。

  1. cglib 动态代理示例
  2. 代理类分析
  3. Fastclass 机制分析

一、cglib 动态代理示例  


public class Target{
    public void f(){
        System.out.println("Target f()");
    }
    public void g(){
        System.out.println("Target g()");
    }
}

public class Interceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args,    MethodProxy proxy) throws Throwable {
        System.out.println("I am intercept begin");
//Note: 此处一定要使用proxy的invokeSuper方法来调用目标类的方法
        proxy.invokeSuper(obj, args);
        System.out.println("I am intercept end");
        return null;
    }
}

public class Test {
    public static void main(String[] args) {
    System.setProperty(DebugginGClassWriter.DEBUG_LOCATioN_PROPERTY, "F:\\code");
         //实例化一个增强器,也就是cglib中的一个class generator
        Enhancer eh = new Enhancer();
         //设置目标类
        eh.setSuperclass(Target.class);
        // 设置拦截对象
        eh.setCallback(new Interceptor());
        // 生成代理类并返回一个实例
        Target t = (Target) eh.create();
        t.f();
        t.g();
    }
}

运行结果为:

I am intercept begin
Target f()
I am intercept end
I am intercept begin
Target g()
I am intercept end 

jdk动态代理相比,cglib可以实现对一般类的代理而无需实现接口。在上例中通过下列步骤来生成目标类Target的代理类:

  1. 创建Enhancer实例
  2. 通过setSuperclass方法来设置目标类
  3. 通过setCallback 方法来设置拦截对象
  4. create方法生成Target的代理类,并返回代理类的实例

二、代理类分析

      在示例代码中我们通过设置DebuggingClassWriter.DEBUG_LOCATION_PROPERTY的属性值来获取cglib生成的代理类。通过之前分析的命名规则我们可以很容易的在F:\\code下面找到生成的代理类 Target$$EnhancerByCGLIB$$788444a0.class 。

使用jd-gui进行反编译(由于版本的问题,此处只能显示部分代码,可以结合javap的反编译结果来进行分析),由于cglib会代理Object中的finalize,equals, toString,hashCode,clone方法,为了清晰的展示代理类我们省略这部分代码,反编译的结果如下:


public class Target$$EnhancerByCGLIB$$788444a0 extends Target implements Factory
{
	private Boolean CGLIB$BOUND;
	private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
	private static final Callback[] CGLIB$STATIC_CALLBACKS;
	private MethodInterceptor CGLIB$CALLBACK_0;
	private static final Method CGLIB$g$0$Method;
	private static final MethodProxy CGLIB$g$0$Proxy;
	private static final Object[] CGLIB$emptyArgs;
	private static final Method CGLIB$f$1$Method;
	private static final MethodProxy CGLIB$f$1$Proxy;
	static void CGLIB$STATICHOOK1()
	{
		CGLIB$THREAD_CALLBACKS = new ThreadLocal();
		CGLIB$emptyArgs = new Object[0];
		Class localClass1 = Class.forName("net.sf.cglib.test.Target$$EnhancerByCGLIB$$788444a0");
		Class localClass2;
		Method[] tmp60_57 = ReflectUtils.findMethods(new String[] { "g", "()V", "f", "()V" }, (localClass2 = Class.forName("net.sf.cglib.test.Target")).getDeclaredMethods());
		CGLIB$g$0$Method = tmp60_57[0];
		CGLIB$g$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "g", "CGLIB$g$0");
		CGLIB$f$1$Method = tmp60_57[1];
		CGLIB$f$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "f", "CGLIB$f$1");
	}
	final void CGLIB$g$0()
	{
		super.g();
	}
	public final void g()
	{
		MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
		if (tmp4_1 == null)
		{
			CGLIB$BIND_CALLBACKS(this);
			tmp4_1 = this.CGLIB$CALLBACK_0;
		}
		if (this.CGLIB$CALLBACK_0 != null) {
			tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy);
		} else{
			super.g();
		}
	}
}

      代理类(Target$$EnhancerByCGLIB$$788444a0)继承了目标类(Target),至于代理类实现的factory接口与本文无关,残忍无视。代理类为每个目标类的方法生成两个方法,例如针对目标类中的每个非private方法,代理类会生成两个方法,以g方法为例:一个是@Override的g方法,一个是CGLIB$g$0(CGLIB$g$0相当于目标类的g方法)。我们在示例代码中调用目标类的方法t.g()时,实际上调用的是代理类中的g()方法。接下来我们着重分析代理类中的g方法,看看是怎么实现的代理功能。

      当调用代理类的g方法时,先判断是否已经存在实现了MethodInterceptor接口的拦截对象,如果没有的话就调用CGLIB$BIND_CALLBACKS方法来获取拦截对象,CGLIB$BIND_CALLBACKS的反编译结果如下:


private static final void CGLIB$BIND_CALLBACKS(java.lang.Object);
  Code:
   0:   aload_0
   1:   checkcast       #2; //class net/sf/cglib/test/Target$$EnhancerByCGLIB$$788444a0
   4:   astore_1
   5:   aload_1
   6:   getfield        #212; //Field CGLIB$BOUND:Z
   9:   ifne    52
   12:  aload_1
   13:  iconst_1
   14:  putfield        #212; //Field CGLIB$BOUND:Z
   17:  getstatic       #24; //Field CGLIB$THREAD_CALLBACKS:Ljava/lang/ThreadLocal;
   20:  invokevirtual   #215; //Method java/lang/ThreadLocal.get:()Ljava/lang/Object;
   23:  dup
   24:  ifnonnull       39
   27:  pop
   28:  getstatic       #210; //Field CGLIB$STATIC_CALLBACKS:[Lnet/sf/cglib/proxy/Callback;
   31:  dup
   32:  ifnonnull       39
   35:  pop
   36:  Goto    52
   39:  checkcast       #216; //class "[Lnet/sf/cglib/proxy/Callback;"
   42:  aload_1
   43:  swap
   44:  iconst_0
   45:  aaload
   46:  checkcast       #48; //class net/sf/cglib/proxy/MethodInterceptor
   49:  putfield        #36; //Field CGLIB$CALLBACK_0:Lnet/sf/cglib/proxy/MethodInterceptor;
   52:  return

为了方便阅读,等价的代码如下:


private static final void CGLIB$BIND_CALLBACKS(Object o){
        Target$$EnhancerByCGLIB$$788444a0 temp_1 = (Target$$EnhancerByCGLIB$$788444a0)o;
        Object temp_2;
        Callback[] temp_3
        if(temp_1.CGLIB$BOUND == true){
            return;
        }
        temp_1.CGLIB$BOUND = true;
        temp_2 = CGLIB$THREAD_CALLBACKS.get();
        if(temp_2!=null){
            temp_3 = (Callback[])temp_2;
        }
        else if(CGLIB$STATIC_CALLBACKS!=null){
            temp_3 = CGLIB$STATIC_CALLBACKS;
        }
        else{
            return;
        }
        temp_1.CGLIB$CALLBACK_0 = (MethodInterceptor)temp_3[0];
        return;
    }

CGLIB$BIND_CALLBACKS 先从CGLIB$THREAD_CALLBACKS中get拦截对象,如果获取不到的话,再从CGLIB$STATIC_CALLBACKS来获取,如果也没有则认为该方法不需要代理。

那么拦截对象是如何设置到CGLIB$THREAD_CALLBACKS 或者 CGLIB$STATIC_CALLBACKS中的呢?

在Jdk动态代理中拦截对象是在实例化代理类时由构造函数传入的,在cglib中是调用Enhancer的firstInstance方法来生成代理类实例并设置拦截对象的。firstInstance的调用轨迹为:

  1. Enhancer:firstInstance
  2. Enhancer:createUsingReflection
  3. Enhancer:setThreadCallbacks
  4. Enhancer:setCallbacksHelper
  5. Target$$EnhancerByCGLIB$$788444a0 : CGLIB$SET_THREAD_CALLBACKS

 在第5步,调用了代理类的CGLIB$SET_THREAD_CALLBACKS来完成拦截对象的注入。下面我们看一下CGLIB$SET_THREAD_CALLBACKS的反编译结果:


public static void CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[]);
  Code:
   0:   getstatic       #24; //Field CGLIB$THREAD_CALLBACKS:Ljava/lang/ThreadLocal;
   3:   aload_0
   4:   invokevirtual   #207; //Method java/lang/ThreadLocal.set:(Ljava/lang/Object;)V
   7:   return

在CGLIB$SET_THREAD_CALLBACKS方法中调用了CGLIB$THREAD_CALLBACKS的set方法来保存拦截对象,在CGLIB$BIND_CALLBACKS方法中使用了CGLIB$THREAD_CALLBACKS的get方法来获取拦截对象,并保存到CGLIB$CALLBACK_0中。这样,在我们调用代理类的g方法时,就可以获取到我们设置的拦截对象,然后通过  tmp4_1.intercept(this, CGLIB$g$0$Method, CGLIB$emptyArgs, CGLIB$g$0$Proxy)  来实现代理。这里来解释一下intercept方法的参数含义:

@para1 obj :代理对象本身

@para2 method : 被拦截的方法对象

@para3 args:方法调用入参

@para4 proxy:用于调用被拦截方法的方法代理对象

这里会有一个疑问,为什么不直接反射调用代理类生成的(CGLIB$g$0)来间接调用目标类的被拦截方法,而使用proxy的invokeSuper方法呢?这里就涉及到了另外一个点— FastClass 。

三、Fastclass 机制分析

     Jdk动态代理的拦截对象是通过反射的机制来调用被拦截方法的,反射的效率比较低,所以cglib采用了FastClass的机制来实现对被拦截方法的调用。FastClass机制就是对一个类的方法建立索引,通过索引来直接调用相应的方法,下面用一个小例子来说明一下,这样比较直观


public class test10 {
    public static void main(String[] args){
        Test tt = new Test();
        Test2 fc = new Test2();
        int index = fc.getIndex("f()V");
        fc.invoke(index, tt, null);
    }
}

class Test{
    public void f(){
        System.out.println("f method");
    }
    
    public void g(){
        System.out.println("g method");
    }
}
class Test2{
    public Object invoke(int index, Object o, Object[] ol){
        Test t = (Test) o;
        switch(index){
        case 1:
            t.f();
            return null;
        case 2:
            t.g();
            return null;
        }
        return null;
    }
    
    public int getIndex(String signature){
        switch(signature.hashCode()){
        case 3078479:
            return 1;
        case 3108270:
            return 2;
        }
        return -1;
    }
}

上例中,Test2是Test的Fastclass,在Test2中有两个方法getIndex和invoke。在getIndex方法中对Test的每个方法建立索引,并根据入参(方法名+方法的描述符)来返回相应的索引。Invoke根据指定的索引,以ol为入参调用对象O的方法。这样就避免了反射调用,提高了效率。代理类(Target$$EnhancerByCGLIB$$788444a0)中与生成Fastclass相关的代码如下:


Class localClass1 = Class.forName("net.sf.cglib.test.Target$$EnhancerByCGLIB$$788444a0");
localClass2 = Class.forName("net.sf.cglib.test.Target");
CGLIB$g$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "g", "CGLIB$g$0");

MethodProxy中会对localClass1和localClass2进行分析并生成FastClass,然后再使用getIndex来获取方法g 和 CGLIB$g$0的索引,具体的生成过程将在后续进行介绍,这里介绍一个关键的内部类:


private static class FastClassInfo
    {
        FastClass f1; // net.sf.cglib.test.Target的fastclass
        FastClass f2; // Target$$EnhancerByCGLIB$$788444a0 的fastclass
        int i1; //方法g在f1中的索引
        int i2; //方法CGLIB$g$0在f2中的索引
    }

MethodProxy 中invokeSuper方法的代码如下:


    FastClassInfo fci = fastClassInfo;
    return fci.f2.invoke(fci.i2, obj, args);

当调用invokeSuper方法时,实际上是调用代理类的CGLIB$g$0方法,CGLIB$g$0直接调用了目标类的g方法。所以,在第一节示例代码中我们使用invokeSuper方法来调用被拦截的目标类方法。

至此,我们已经了解cglib动态代理的工作原理,接下来会对cglib的相关源码进行分析。

以上就是Java cglib动态代理原理分析的详细内容,更多关于Java cglib动态代理原理的资料请关注编程网其它相关文章!

--结束END--

本文标题: Java cglib动态代理原理分析

本文链接: https://lsjlt.com/news/125684.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java cglib动态代理原理分析
    目录一、cglib 动态代理示例  二、代理类分析 三、Fastclass 机制分析 本文分下面三个部分来分析cglib动态代理的原理。 cglib&n...
    99+
    2024-04-02
  • Spring中JDK和cglib动态代理原理的示例分析
    这篇文章给大家分享的是有关Spring中JDK和cglib动态代理原理的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Java代理介绍Java中代理的实现一般分为三种:JDK静态代理、JDK动态代理以及C...
    99+
    2023-06-02
  • java代理模式(静态代理、动态代理、cglib代理)
    目录代理模式静态代理代码接口被代理对象代理对象测试动态代理代码:接口目标对象代理对象测试cglib代理代码:目标对象代理对象测试应用总结代理模式 代理模式(Proxy Pattern...
    99+
    2024-04-02
  • java动态代理(jdk与cglib)详细解析
    JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息...
    99+
    2022-11-15
    java jdk cglib
  • Java CGLib动态代理机制(全面解析)
    一、首先说一下JDK中的动态代理:JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编...
    99+
    2023-05-31
    java 动态代理 cglib
  • jdk动态代理和cglib动态代理详解
    目录静态代理基于继承的方式实现静态代理基于聚合的方式实现静态代理继承与聚合方式实现的静态代理对比动态代理JDK动态代理如何实现一个HashMap的动态代理类?Cglib动态代理JDK...
    99+
    2024-04-02
  • 怎么在Java中动态代理Cglib
    这篇文章将为大家详细讲解有关怎么在Java中动态代理Cglib,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。常用的java框架有哪些1.SpringMVC,Spring Web MVC是一种...
    99+
    2023-06-14
  • 深入浅析java 中的JDK与cglib动态代理
    这篇文章将为大家详细讲解有关深入浅析java 中的JDK与cglib动态代理,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。java 动态代理实例详解1.jdk动态代理 package com...
    99+
    2023-05-31
    java jdk cglib
  • Java基础之动态代理Cglib详解
    目录一、前言二、服务三、代理工厂四、结果一、前言 经测试,jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适...
    99+
    2024-04-02
  • spring cglib 与 jdk 动态代理
    1. 概述JDK动态代理是利用java反射机制 生成一个实现接口的匿名类, 在调用具体方法前调用InvocationHandler来处理Cglib动态代理是 利用asm开源包 把被代理类的class文件加载进来 通过修改其字节码生成子类来处...
    99+
    2023-05-31
    spring cglib jdk
  • Java反射(JDK)与动态代理(CGLIB)详解
    目录一、反射二、动态代理1、JDK代理2、CGLIB代理3、JDK代理与CGLIB代理对比总结一、反射 概念:在运行状态中,对于任意的一个类,都能够知道这个类的所有字段和方法,对任意...
    99+
    2024-04-02
  • 怎么在java中实现CGLIB动态代理
    怎么在java中实现CGLIB动态代理?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Java的特点有哪些Java的特点有哪些1.Java语言作为静态面向对象编程语言的代表,...
    99+
    2023-06-14
  • JDK动态代理和CGLIB区别
    JDK动态代理和CGLIB区别1、JDK动态代理:利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。(相关视频教程分享:java视频...
    99+
    2020-11-10
    java教程 JDK CGLIB 区别
  • Spring之AOP两种代理机制对比分析(JDK和CGLib动态代理)
    目录Spring AOP两种代理机制对比JDK动态代理CGLib动态代理SpringAOP两种代理原理SpringAOP代理JDK动态代理CGLIB代理两者对比使用注意总结Sprin...
    99+
    2023-05-19
    Spring AOP AOP代理机制 JDK动态代理 CGLib动态代理
  • Java动态代理与静态代理实例分析
    这篇文章主要介绍了Java动态代理与静态代理实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java动态代理与静态代理实例分析文章都会有所收获,下面我们一起来看看吧。一、静态代理静态代理的使用静态代理,代...
    99+
    2023-07-02
  • Java JDK与cglib动态代理有哪些区别
    本篇内容主要讲解“Java JDK与cglib动态代理有哪些区别”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java JDK与cglib动态代理有哪些区别”吧!一、说明sp...
    99+
    2023-07-05
  • Java动态代理的原理
    Java动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。(推荐:java视频教程)代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还...
    99+
    2015-03-30
    Java
  • 通俗易懂 快速理解 JDK动态代理 和 cglib动态代理
    动态代理的实现方案有两种,JDK动态代理和CGLIB动态代理,区别在于JDK自带的动态代理,必须要有接口,而CGLIB动态代理有没有接口都可以。 JDK动态代理:JDK原生的实现方式,需要被代理的...
    99+
    2023-10-21
    java spring
  • Java动态代理示例分析
    这篇文章主要讲解了“Java动态代理示例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java动态代理示例分析”吧!定义动态代理指的是,代理类和目标类的关系在程序运行的时候确定的,客户通...
    99+
    2023-06-29
  • Java深入分析动态代理
    目录代理模式静态代理动态代理JDK动态代理CGLIB动态代理JDK代理与CGLIB代理的区别"代理"这个词相信大家并不陌生,简单来说就是代替厂家来售卖商品,代理替...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作