iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >关于Java的动态代理机制
  • 663
分享到

关于Java的动态代理机制

Java代理Java动态代理 2023-05-19 20:05:24 663人浏览 八月长安

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

摘要

目录静态代理功能接口功能提供者功能代理者探索动态代理实现机制静态代理 常规的代理模式有以下三个部分组成: 功能接口 interface IFunction { void doATh

静态代理

常规的代理模式有以下三个部分组成:

功能接口

interface IFunction {
	void doAThing();
}

功能提供者

class FunctionProvider implement IFunction {
	public void doAThing {
		System.out.print("do A");
	}
}

功能代理者

class Proxy implement IFunction {
	private FunctionProvider provider;
	Proxy(FunctionProvider provider) {
		this.provider = provider;
	}
	public void doAThing {
		provider.doAThing();
	}
}

前两者就是普通的接口和实现类,而第三个就是所谓的代理类。对于使用者而言,他会让代理类去完成某件任务,并不关心这件任务具体的跑腿者。

这就是静态代理,好处是方便调整变换具体实现类,而使用者不会受到任何影响。

不过这种方式也存在弊端:比如有多个接口需要进行代理,那么就要为每一个功能提供者创建对应的一个代理类,那就会越来越庞大。而且,所谓的“静态”代理,意味着必须提前知道被代理的委托类。

通过下面一个例子来说明下:

统计函数耗时–静态代理实现

现在希望通过一个代理类,对我感兴趣的方法进行耗时统计,利用静态代理有如下实现:

interface IAFunc {
	void doA();
}
interface IBFunc {
	void doB();
}
class TimeConsumeProxy implement IAFunc, IBFunc {
	private AFunc a;
	private BFunc b;
	public(AFunc a, BFunc b) {
		this.a = a;
		this.b = b;
	}
	void doA() {
		long start = System.currentMillions();
		a.doA();
		System.out.println("耗时:" + (System.currentMillions() - start));
	}
	void doB() {
		long start = System.currentMillions();
		b.doB();
		System.out.println("耗时:" + (System.currentMillions() - start));
	}
}

弊端很明显,如果接口越多,每新增一个函数都要去修改这个 TimeConsumeProxy 代理类:把委托类对象传进去,实现接口,在函数执行前后统计耗时。

这种方式显然不是可持续性的,下面来看下使用动态代理的实现方式,进行对比。 ###动态代理

动态代理的核心思想是通过 Java Proxy 类,为传入进来的任意对象动态生成一个代理对象,这个代理对象默认实现了原始对象的所有接口。

还是通过统计函数耗时例子来说明更加直接。

统计函数耗时–动态代理实现

interface IAFunc {
	void doA();
}
interface IBFunc {
	void doB();
}
class A implement IAFunc { ... }
class B implement IBFunc { ... }
class TimeConsumeProxy implements InvocationHandler {
	private Object realObject;
	public Object bind(Object realObject) {
		this.realObject = realObject;
		Object proxyObject = Proxy.newInstance(
			realObject.getClass().getClassLoader(),
			realObject.getClass().getInterfaces(),
			this
		);
		return proxyObject;
	}
	@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentMillions();
        Object result = method.invoke(target, args);
        System.out.println("耗时:" + (System.currentMillions() - start));
        return result;
    }
}

具体使用时:

public static void main(String[] args) {
	A a = new A();
	IAFunc aProxy = (IAFunc) new TimeConsumeProxy().bind(a);
	aProxy.doA();
	B b = new B();
	IBFunc bProxy = (IBFunc) new TimeConsumeProxy().bind(b);
	bProxy.doB();	
}

这里最大的区别就是:代理类和委托类 互相透明独立,逻辑没有任何耦合,在运行时才绑定在一起。这也就是静态代理与动态代理最大的不同,带来的好处就是:无论委托类有多少个,代理类不受到任何影响,而且在编译时无需知道具体委托类。

回到动态代理本身,上面代码中最重要的就是:

Object proxyObject = Proxy.newInstance(
			realObject.getClass().getClassLoader(),
			realObject.getClass().getInterfaces(),
			this
		);

通过 Proxy 工具,把真实委托类转换成了一个代理类,最开始提到了一个代理模式的三要素:功能接口、功能提供者、功能代理者;在这里对应的就是:realObject.getClass().getInterfaces()realObjectTimeConsumeProxy

其实动态代理并不复杂,通过一个 Proxy 工具,为委托类的接口自动生成一个代理对象,后续的函数调用都通过这个代理对象进行发起,最终会执行到 InvocationHandler#invoke 方法,在这个方法里除了调用真实委托类对应的方法,还可以做一些其他自定义的逻辑,比如上面的运行耗时统计等。

探索动态代理实现机制

好了,上面我们已经把动态代理的基本用法及为什么要用动态代理进行了讲解,很多文章到这里也差不多了,不过我们还准备进一步探索一下给感兴趣的读者。

抛出几个问题:

上面生成的代理对象 Object proxyObject 究竟是个什么东西?为什么它可以转型成 IAFunc,还能调用doA() 方法? 这个 proxyObject 是怎么生成出来的?它是一个 class 吗?

下面我先给出答案,再一步步探究这个答案是如何来的。 问题一: proxyObject 究竟是个什么 -> 动态生成的 $Proxy0.class 文件

在调用 Proxy.newInstance 后,Java 最终会为委托类 A 生成一个真实的 class 文件:$Proxy0.class,而 proxyObject 就是这个 class 的一个实例。

猜一下,这个 $Proxy0.class 类长什么样呢,包含了什么方法呢?回看下刚刚的代码:

IAFunc aProxy = (IAFunc) new TimeConsumeProxy().bind(a);
aProxy.doA();

推理下,显然这个 $Proxy0.class 实现了 IAFunc 接口,同时它内部也实现了 doA() 方法,而且重点是:这个 doA() 方法在运行时会执行到 TimeConsumeProxy#invoke() 方法里。

重点来了!下面我们来看下这个 $Proxy0.class 文件,把它放进 IDE 反编译下,可以看到如下内容,来验证下刚刚的猜想:

final class $Proxy0 extends Proxy implements IAFunc {
    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  {
        // 省略
    }
    public final void doA() throws  {
        try {
        	// 划重点
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final String toString() throws  {
        // 省略
    }
    public final int hashCode() throws  {
        // 省略
    }
    static {
        try {
        	// 划重点
            m3 = Class.forName("proxy.IAFunc").getMethod("doA", new Class[0]);
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

没错,刚刚的猜想都中了!实现了 IAFunc 接口和 doA() 方法,不过,doA()里是什么鬼?

super.h.invoke(this, m3, (Object[])null);

回看下,TimeConsumeProxy里面的 invoke 方法,它的函数签名是啥?

public Object invoke(Object proxy, Method method, Object[] args);

没错,doA()里做的就是调用 TimeConsumeProxy#invoke() 方法。

那么也就是说,下面这段代码执行流程如下:

IAFunc aProxy = (IAFunc) new TimeConsumeProxy().bind(a);aProxy.doA();

基于传入的委托类 A,生成一个$Proxy0.class 文件; 创建一个 $Proxy0.class 对象,转型为 IAFunc 接口; 调用 aProxy.doA() 时,自动调用 TimeConsumeProxy 内部的 invoke 方法。

问题二:proxyObject 是怎么一步步生成出来的 -> $Proxy0.class 文件生成流程

刚刚从末尾看了结果,现在我们回到代码的起始端来看:

Object proxyObject = Proxy.newInstance(
			realObject.getClass().getClassLoader(),
			realObject.getClass().getInterfaces(),
			this
		);

准备好,开始发车读源码了。我会截取重要的代码并加上注释。

先看Proxy.newInstance():

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) {
    // 复制要代理的接口                                  	
	final Class<?>[] intfs = interfaces.clone();
	// 重点:生成 $Proxy0.class 文件并通过 ClassLoader 加载进来
	Class<?> cl = getProxyClass0(loader, intfs);
	// 对 $Proxy0.class 生成一个实例,就是 `proxyObject`
	final Constructor<?> cons = cl.getConstructor(constructorParams);
	return cons.newInstance(new Object[]{h});
}

再来看 getProxyClass0 的具体实现:ProxyClassFactory工厂类:

@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
	// 参数为 ClassLoader 和要代理的接口
	Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
	// 1. 验证 ClassLoader 和接口有效性
	for (Class<?> intf : interfaces) {
		// 验证 classLoader 正确性
		Class<?> interfaceClass = Class.forName(intf.getName(), false, loader);
		if (interfaceClass != intf) {
            throw new IllegalArgumentException(
                intf + " is not visible from class loader");
        }
		// 验证传入的接口 class 有效
		if (!interfaceClass.isInterface()) { ... } 
		// 验证接口是否重复
		if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { ... }
	}
	// 2. 创建包名及类名 $Proxy0.class
	proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
	long num = nextUniqueNumber.getAndIncrement();
    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    // 3. 创建 class 字节码内容
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
    // 4. 基于字节码和类名,生成 Class<?> 对象
    return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
}

再看下第三步生成 class 内容 ProxyGenerator.generateProxyClass

// 添加 hashCode equals toString 方法
addProxyMethod(hashCodeMethod, Object.class);
addProxyMethod(equalsMethod, Object.class);
addProxyMethod(toStringMethod, Object.class);
// 添加委托类的接口实现
for (int i = 0; i < interfaces.length; i++) {
    Method[] methods = interfaces[i].getMethods();
    for (int j = 0; j < methods.length; j++) {
         addProxyMethod(methods[j], interfaces[i]);
    }
}
// 添加构造函数
methods.add(this.generateConstructor());

这里构造好了类的内容:添加必要的函数,实现接口,构造函数等,下面就是要写入上一步看到的 $Proxy0.class 了。

ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
dout.writeInt(0xCAFEBABE);
...
dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
...
return bout.toByteArray();

到这里就生成了第一步看到的 $Proxy0.class 文件了,完成闭环,讲解完成! ###动态代理小结

通过上面的讲解可以看出,动态代理可以随时为任意的委托类进行代理,并可以在 InvocationHandler#invoke 拿到运行时的信息,并可以做一些切面处理。

在动态代理背后,其实是为一个委托类动态生成了一个 $Proxy0.class 的代理类,该代理类会实现委托类的接口,并把接口调用转发到 InvocationHandler#invoke 上,最终调用到真实委托类的对应方法。

动态代理机制把委托类和代理类进行了隔离,提高了扩展性。 ###Java 动态代理与 python 装饰器

这是 Java 语言提供的一个有意思的语言特性,而其实 Python 里也提供了一种类似的特性:装饰器,可以达到类似的面相切面编程思想,下次有空再把两者做下对比,这次先到这。

到此这篇关于关于Java的动态代理机制的文章就介绍到这了,更多相关Java的动态代理机制内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 关于Java的动态代理机制

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

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

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 关于Java的动态代理机制
    目录静态代理功能接口功能提供者功能代理者探索动态代理实现机制静态代理 常规的代理模式有以下三个部分组成: 功能接口 interface IFunction { void doATh...
    99+
    2023-05-19
    Java 代理 Java 动态代理
  • 详解Java的Proxy动态代理机制
    目录一、Jvm加载对象二、代理模式2.1、基本描述2.2、静动态模式三、静态代理四、动态代理4.1、场景描述4.2、基础API案例4.3、代理类结构4.4、JDK源码五、源代码地址一...
    99+
    2022-11-12
  • Java动态代理机制是什么
    这篇文章主要讲解了“Java动态代理机制是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java动态代理机制是什么”吧!Java动态代理机制的出现,使得Java开发人员不用手工编写代理类...
    99+
    2023-06-17
  • 详解Java Proxy动态代理机制
    一、Jvm加载对象 在说Java动态代理之前,还是要说一下Jvm加载对象的过程,这个依旧是理解动态代理的基础性原理: Java类即源代码程序.java类型文件,经过编译器编译之后就...
    99+
    2022-11-12
  • Java动态代理机制的实例详解
     Java动态代理机制在学习spring的时候,我们知道Spring主要有两大思想,一个是IoC,另一个就是AOP,对于IoC,依赖注入就不用多说了,而对于Spring的核心AOP来说,我们不但要知道怎么通过AOP来满足的我们的功...
    99+
    2023-05-31
    java 动态代理 ava
  • 关于ArrayList的动态扩容机制解读
    目录1. 前言2. ArrayList 的动态扩容机制2.1. ArrayList 的主要属性2.2. ArrayList 的构造器2.3. ArrayList 的动态扩容3. 小结...
    99+
    2022-11-13
    ArrayList的扩容机制 动态扩容机制 ArrayList动态扩容机制
  • Java中动态代理机制的原理是什么
    今天就跟大家聊聊有关Java中动态代理机制的原理是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Java 动态代理机制的出现,使得 Java  开发人员不用手工编写代理...
    99+
    2023-06-17
  • Java CGLib动态代理机制(全面解析)
    一、首先说一下JDK中的动态代理:JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编...
    99+
    2023-05-31
    java 动态代理 cglib
  • 详解Java中的反射机制和动态代理
    目录一、反射概述二、反射之Class类2.1、初识Class类2.2、Class有下面的几个特点2.3、获取Class类实例2.4、关于包装类的静态属性2.5、通过Class类的其他...
    99+
    2022-11-12
  • 深度剖析java中JDK动态代理机制
    摘要相比于静态代理,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定一组接口及目标类对象就能动态的获得代理对象。代理模式使用代理模式必须要让代理类和目标类实现相同的接口,客户端通过代理类来调用目标方法,代理类会将所有的方法调用...
    99+
    2023-05-31
    jdk 动态代理 ava
  • Mybatis基于MapperScan注解的动态代理加载机制详解
    1.如下图在代码开发中使用mybatis时,通过一个接口UserDao对应的方法selectUserNameById执行xml里配置的selectUserNameById查...
    99+
    2023-01-28
    Mybatis动态代理加载 Mybatis MapperScan注解动态代理加载
  • Java反射机制中动态代理的示例分析
    这篇文章主要介绍了Java反射机制中动态代理的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1. 代理模式代理模式就是为其他对象提供一种代理来控制对这个对象的访问。其...
    99+
    2023-06-17
  • 从另一个角度理解Java中的动态代理机制
    本篇文章为大家展示了从另一个角度理解Java中的动态代理机制,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。class文件简介及加载Java编译器编译好Java文件之后,产生.class 文件在磁盘中...
    99+
    2023-05-31
    java 动态代理 ava
  • 关于feign接口动态代理源码解析
    目录feign接口动态代理源码解析@FeignClinet代理类注册feign源码解析Feign的作用源码及流程介绍feign接口动态代理源码解析 @FeignClinet 代理类注...
    99+
    2022-11-13
  • Java中反射机制和动态代理的示例分析
    这篇文章主要介绍了Java中反射机制和动态代理的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、反射概述反射机制指的是Java在运行时候有一种自观的能力,能够了解自...
    99+
    2023-06-15
  • 在java项目中运用动态代理机制的方法
    在java项目中运用动态代理机制的方法?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。java 中动态代理机制的实例讲解在java的动态代理机制中,有两个重要的类...
    99+
    2023-05-31
    java ava
  • 如何深度剖析Java反射机制中的动态代理原理
    本篇文章给大家分享的是有关如何深度剖析Java反射机制中的动态代理原理,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。动态代理类原理 示例代码参见《Java反射机制剖析:简单谈谈...
    99+
    2023-06-17
  • Java动态代理的原理
    Java动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。(推荐:java视频教程)代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还...
    99+
    2015-03-30
    Java
  • Java中的动态代理
    一、Java中的两种动态代理方式 Java中常用的有两种动态代理方式,分别为:JDK动态代理和Cglib动态代理。 JDK动态代理是通过实现接口的方式完成动态代理。 Cglib动态代理是通过继承目标类或实现接口的方式完成动态代理。 ...
    99+
    2023-09-06
    java Powered by 金山文档
  • Java的动态代理和静态代理详解
    目录0、代理模式1、静态代理2、 加深理解3、动态代理动态代理的例子总结0、代理模式 为什么要学习代理模式?这是SpringAOP的底层【SpringAOP和SpringMVC】 代...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作