首页 > 编程语言 >Java中动态代理技术生成的类与原始类的区别 (good)

Java中动态代理技术生成的类与原始类的区别 (good)

时间:2023-01-02 10:32:30浏览次数:47  
标签:lang good Java java Object 代理 new public

  用动态代理的时候,对它新生成的类长什么样子感到好奇.有幸通过一些资料消除了心里的疑惑.

  平时工作使用的Spring框架里面有一个AOP(面向切面)的机制,只知道它是把类重新生成了一遍,在切面上加上了后来定义的逻辑.这样就达到了动态的在原有类上增加一些功能.比如日志打印,拦截信息等.

  这里只关心动态代理技术生成新的类,先不管虚拟机是如何去生成类,用了什么字节码生成技术,怎么产生字节码等这一系列动作.现在只关心最后生成的新类长什么样,它和老类有什么区别.为了获取到生成后的代理类的字节码并且反编译成我们能够看得懂的代码,需要实现一个动态代理例子.

例子

//接口

package note.com;

public interface IGirl {
void sayHello();
}

 

//接口实现,也是需要利用动态代理扩展功能的类

package note.com;

public class MyGirl implements IGirl {
public void sayHello() {
System.out.println("如花似玉石榴姐");
}
}

 

//代理实现类

package note.com;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyGirl implements InvocationHandler {
Object originalObj;

Object bind(Object originalObj) {
this.originalObj = originalObj;
return Proxy.newProxyInstance(originalObj.getClass()
.getClassLoader(), originalObj.getClass().getInterfaces(),
this);
}

public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("第一美女是:");
return method.invoke(originalObj, args);
}
}

 

//测试类

package note.com;

public class Test {

public static void main(String[] args) {
IGirl hello = (IGirl) new ProxyGirl().bind(new MyGirl());
hello.sayHello();
     System.out.println(hello.getClass().getName());
}

}

 

结果:

第一美女是:
如花似玉石榴姐
com.sun.proxy.$Proxy0

这里可见hello真实类型是$Proxy0,到底它长什么样子,往下看.

代理类字节码反编译结果

package note.com;

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 IGirl {

private static Method m3;
private static Method m1;
private static Method m0;
private static Method m2;

/*
* 构造函数传入能够访问真实对象的代理类,这个实际是上例Test中的new ProxyGirl()
*/
protected $Proxy0(InvocationHandler h) {
super(h);
}

/*
* 代理实现sayHello,
*/
public void sayHello() {
try {
this.h.invoke(this, m3, null);
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}

}

/*
* 代理实现继承自Object的equals
*/
public void equals() {
try {
this.h.invoke(this, m1, null);
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}

/*
* 代理实现继承自Object的hashCode
*/
public int hashCode() {
try {
return (Integer) this.h.invoke(this, m0, null);
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}

/*
* 代理实现继承自Object的toString
*/
public String toString() {
try {
return (String) this.h.invoke(this, m2, null);
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}

/*
* 初始化真实对象中的所有方法
*/
static {
try {
m3 = Class.forName("note.com.IGirl").getMethod("sayHello",
new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("equals",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("equals",
new Class[0]);
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(
localClassNotFoundException.getMessage());
}
}

}

 

  通过观察反编译后的动态类,这个逻辑并不复杂,主要功能是对所有的方法进行初始化,到执行某个方法的时候调用我们自己实现的代理类去执行扩展功能和原始类的方法.

对原始类和动态代理后产生的类进行比较:

1,$Proxy0访问真实的类对象通过InvocationHandler的实现类调用.

2,动态代理扩展功能并没有在$Proxy0中加入,而是回调InvocationHandler的接口,通过子类实现Invoke方法扩展.

 

从调用关系上看使用动态代理前后:

Java中动态代理技术生成的类与原始类的区别 (good)_字节码

左边:是原始的调用关系,原始类中有什么逻辑就执行什么.

右边:是动态代理以后,通过动态代理生成类的对象调用代理类,代理类调用扩展逻辑,然后调用原始类对象的逻辑.由此实现了对原始类的动态扩展.

  通过这样追本溯源的去了解,我对动态代理的理解更加深刻,也打消了心里的一个疑惑.

 

 

ps:

动态代理什么时候用?可以参考这个:​​动态代理技术实现设计模式-代理模式​​

文中的字节码反编译是参考<<深入理解Java虚拟机 JVM高级特性与最佳实践>>这本书.

 



标签:lang,good,Java,java,Object,代理,new,public
From: https://blog.51cto.com/u_15147537/5983456

相关文章

  • 第十章《日期与时间》第3节:Java8新日期时间系统简介
    ​Date和Calender都是早期Java项目中用于处理日期和时间的工具类,这两个类在设计上有一些明显的缺陷,这些缺陷主要包括:用于计算日期时间的方法较少、线程不安全、对象中的月份......
  • Solon Java Framework v1.12.0 发布
    一个更现代感的Java应用开发框架:更快、更小、更自由。没有Spring,没有Servlet,没有JavaEE;独立的轻量生态。主框架仅0.1MB。@ControllerpublicclassApp{publ......
  • SpringBoot中使用JDBC(扩展:关于java中的jdbc、数据库驱动、数据库连接池的学习与理解)
    SpringBoot中使用JDBC:https://huaweicloud.csdn.net/63876ea0dacf622b8df8bf7d.html?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7E......
  • mysql-connector-java与mysql以及JDK的对应版本
    https://blog.csdn.net/xunxue1523/article/details/105524758?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ER......
  • nginx-clojure nginx clojure & java & groovy 模块
    nginx-clojure是一个nginx扩展模块,让我们可以直接运行clojure&java&groovy,还是比较强大的,支持的功能也不少我们可以直接基于jvm对于nginx进行扩展了,还是值得尝试......
  • 包机制及java生成文档
    包机制为了更好地组织类,Java提供了包机制,用于区别类名的命名空间。包机制的语法格式为:packagepkg1[.pkg2[.pkg3...]];$\color{red}{一般利用公司域名倒置作......
  • JavaScript(数据类型)
    一、数据类型1.概述为了便于把数据分成所需内存大小不同的数据,充分利用存储空间,于是定义了不同的数据类型。2.变量的数据类型varage=10;//数字型varareYouOk......
  • java中的HashMap类的遍历
    示例代码如下:1publicclassHashMapBianLiTest{2publicstaticvoidmain(String[]args){3//hashMap的遍历4HashMaphashMap=new......
  • Linux 中 java 访问 windows共享目录
    有两个方案(本文介绍方案1的使用)1、将windows共享目录,挂载到linux系统下,通过使用本地目录访问windows共享目录2、通过samba的java实现包,不过需要开个windows共享目录的账......
  • Java序列化和反序列化
    遇到这个JavaSerializable序列化这个接口,我们可能会有如下的问题a,什么叫序列化和反序列化b,作用。为啥要实现这个Serializable接口,也就是为啥要序列化c,serialVersion......