当前位置:首页 > CN2资讯 > 正文内容

理解java动态代理java 动态代理

1天前CN2资讯

理解java动态代理

java动态代理主要用来做方法的增强,让你可以在不修改原来代码的情况下,增强一些方法,也就是在原来的方法执行前后做任何你想做的事情。具体应用的话,比如可以添加调用日志,做事务控制等。Spring AOP的底层原理就是利用了动态代理来实现的。

动态代理的实现一般有两种方式:

  • JDK动态代理,当目标类实现了接口,我们可以使用jdk的Proxy来生成代理对象
  • 使用CGLIB生成代理,CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。

本次先看看JDK动态代理如何实现,网上有很多的代码实例可参考。使用JDK动态代理前提是 目标类必须实现了某个接口

  • 接口
public interface Subject {
public void doSomething();
}

  • 接口实现类
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println( "call doSomething()" );
}
}

  • 代理类的实现
public class ProxyHandler implements InvocationHandler {
private Object targetObject;
public Object createProxyInstance(Object targetObject){
this.targetObject = targetObject;
/*
* 第一个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器
* 第二个参数设置代理类实现的接口
* 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法
*/
return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
this.targetObject.getClass().getInterfaces(),
this);
}

public Object invoke( Object realProxy, Method method, Object[] args ) throws Throwable
{
System.out.println(" param Object proxy is " + realProxy.getClass().getName());
//在转调具体目标对象之前,可以执行一些功能处理
before();
//转调具体目标对象的方法
Object obj = method.invoke(targetObject, args);
//在转调具体目标对象之后,可以执行一些功能处理
after();
return obj;
}

public void before(){
System.out.println("before method.invoke ...");
}

public void after(){
System.out.println("after method.invoke ...");
}

重点关注 createProxyInstance 方法,它返回真正的在内存中创建的代理类对象,具体查看反编译代码发现是如下形态:

public final class $Proxy0 extends Proxy implements Subject

后面分析这个createProxyInstance 方法返回的具体代理类。

  • 代理类测试
public class DynamicProxy {
public static void main(String[] args){
// 被代理的实际对象
Subject targetObject = new RealSubject();
// handler对象作用是用于在运行时jvm创建实际代理类对象的依据
ProxyHandler handler = new ProxyHandler();
// 利用Proxy.newProxyInstance 创建一个代理类对象
Subject proxySubject = (Subject) handler.createProxyInstance(targetObject);
// [代理类对象] 执行 [被代理的实际对象] 的doSomething方法
proxySubject.doSomething();
// write proxySubject class binary data to file
createProxyClassFile();
}

// 将真正的代理类以class字节码文件形式展现出来,就是内存中真正执行代理操作的类
public static void createProxyClassFile()
{
String name = "ProxySubject";
byte[] data = ProxyGenerator.generateProxyClass(name, RealSubject.class.getInterfaces());
try
{
File file = new File("E:\\"+name + ".class");
if (file.exists()){
file.delete();
}
FileOutputStream out = new FileOutputStream(file);
out.write( data );
out.close();
}
catch( Exception e )
{
e.printStackTrace();
}
}
}

  • 运行DynamicProxy 的 main 方法可在控制台看到如下信息
    param Object proxy is com.sun.proxy.$Proxy0
    before method.invoke …
    call doSomething()
    after method.invoke …

并且在E盘下生成一个class字节码文件 ProxySubject.class 它就是 proxy iscom.sun.proxy.$Proxy0,当然输出的时候修改它的名称为ProxySubject。

param Object proxy is com.sun.proxy.$Proxy0这行输出信息也表示 ProxyHandler类中的方法 public Object invoke( Object realProxy, Method method, Object[] args ) 的第一个参数 Object realProxy 其实就是运行期间jvm动态生成的代理类对象 $Proxy。

反编译代理类 $Proxy 即 刚刚E盘下生成的文件 ProxySubject.class 查看实际执行的代理类究竟是什么鬼。

部分编译代码如下:

public final class ProxySubject extends Proxy implements Subject
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;

public ProxySubject(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}

public final boolean equals(Object paramObject)
{
// .... 省略
}

public final void doSomething()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}

public final String toString()
{
// .... 省略
}

public final int hashCode()
{
// .... 省略
}

static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("cn.guzt.utils.Subject").getMethod("doSomething", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}

注意上面的构造函数 中的参数paramInvocationHandler,其实我们创建的ProxyHandler对象,jvm创建动态代理类对象时自动将ProxyHandler对象传入。

该代理类 extends Proxy implements Subject,其中Proxy中含有 成员对象 protected InvocationHandler h; 而 此时 的h就是 paramInvocationHandler对象也即我们创建的ProxyHandler对象。

另外该代理类实现了Subject接口,所以我们看到测试类中DynamicProxy的代码:

Subject proxySubject = (Subject) handler.createProxyInstance(targetObject);

这下明白为什么handler.createProxyInstance(targetObject)可以被强制转换为Subject。

最后内存中创建的代理类执行 doSomething()方法时,跑到了 我们创建的ProxyHandler对象中执行invoke方法,invoke方法中的Method 参数就是被代理对象类中的doSomething()方法的反射。利用反射技术执行doSomething方法。

顺便说下 ProxyHandler中invoke方法的第一个参数之前说过了,它是我们实际代理类对象com.sun.proxy.$Proxy0 ,这个参数其实没有什么特别用处,只是我们反编译过来看看用的。

到此一个基本的代理流程完毕,其实我感觉 jvm动态代理方式的实现比较。。。不是那么让人顺畅的理解,你创建的public class ProxyHandler implements InvocationHandler 并不是真正的代理执行者,而是jvm根据你创建的ProxyHandler又在内存中创建了一个真正的代理执行者com.sun.proxy.$Proxy0 ,这个真正的代理执行者其实最后还是调用了你创建的ProxyHandler对象中的invoke方法,只不过com.sun.proxy.$Proxy0 的作用就是在其内部将需要反射 处理等杂活给做了。

    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/25024.html

    分享给朋友:

    “理解java动态代理java 动态代理” 的相关文章

    RackNerd主机服务评测:高性价比与卓越客户体验

    RackNerd是一家自2019年成立以来便迅速崛起的美国主机商。每当我想起这家公司,心中总是浮现出他们以高性价比著称的形象。初次接触时,我对他们的服务种类印象深刻:虚拟主机、VPS主机、独立服务器和服务器托管等。这些服务能满足不同行业和客户的需求,尤其是对预算有限的小型企业或创业者而言,RackN...

    Zolerani云服务器评测与市场分析:高性价比的选择

    Zolerani是一个充满活力的云服务器品牌,属于葡萄牙的HLISTAN ZOLERANI, UNIPESSOAL LDA公司旗下。最近,我对这个品牌进行了深入的研究,发现它在全球VPS云服务器市场上取得了显著的发展。Digitalcloud是Zolerani的主打品牌,于2023年2月正式成立,专...

    解决Windows无法使用复制粘贴功能的实用方法

    在计算机使用中,Windows的复制粘贴功能是我们高效工作与学习的得力助手。从文字处理到文件管理,无论是在文档编辑中提取关键信息,还是将图片或文件快速转移到其他地方,复制粘贴都简化了许多操作。它轻松地帮助我们完成任务,节省了宝贵的时间。 我经常在工作中使用复制粘贴,特别是在撰写报告或为项目汇总资料时...

    宝塔面板PHP扩展新增和配置指南

    宝塔面板作为一款功能强大的服务器管理工具,不仅支持Linux,还支持Windows系统。这使得它成为了很多开发者和网站管理员的首选,因为它提供了包括网站管理、数据库管理、文件管理、以及FTP管理等多个方面的功能。最吸引我的地方在于,宝塔面板可以一键安装PHP、MySQL、Nginx等环境,简化了繁琐...

    联通AS4837线路全面解析:稳定性、覆盖范围与国际连接优势

    联通AS4837线路概述 什么是中国联通AS4837线路? 中国联通AS4837线路,往往被称为中国169骨干网,是一条重要的骨干网线路。这条线路自20世纪90年代始建以来,一直在中国的互联网基础设施中扮演着不可或缺的角色。它不仅在国内广泛布置,还连接着多个国际市场,包括香港、美国、日本和韩国等,形...

    获取美国住宅IP的全面指南:确保真实网络体验

    当我提到美国住宅IP地址时,脑海中总会浮现出那些连接到真正家庭网络的IP地址。这些地址并不是随便通过网络数组获取的,而是直接来源于美国的居民家庭网络。这样说来,我们可以简单理解为,美国住宅IP是一种具有真实家庭特征的网络身份。 美国住宅IP的定义并不是一个复杂的概念。它具有纯净度高、不易被识别为机器...