- 浏览: 89406 次
- 性别:
- 来自: 北京
最新评论
-
hpu423:
...
JAVA 字符串常量池 -
hpu423:
[c[flash=200,200][flash=200,200 ...
JAVA 字符串常量池 -
liubang201010:
Foglight 监控OC4j自2004年左右, Oracle ...
OC4J 10.0.3 安装 配置 详解
JDK的动态代理涉及到了JAVA的动态编程,说起来也惭愧,我写了四年的代码,从没用过AOP,我们这破公司的架构太单调,技术太简单,所以一旦走进这样的公司就赶紧走人,要不对个人成长一点帮助都没有,等你工作四年五年的时候就知道做过的项目和用过的技术比个人的智商和学习能力更重要,不说了扯远了。
首先写了一个简单的动态代理:
写完这个动态代理我就开始看Proxy类的源代码,以下的方法也就成了我重点关注的,重点的代码进行了红色加粗,但这还没完:
看到了这里我似乎明白了,原来动态代理是JDK通过反射机制获取了被代理对象的所有接口(动态代理只能实现接口),然后重新生成一个新的类,当然这个类没有保存成文件,而只是在内存里面,这个新的类implements了被代理对象的所有接口,同时他还持有一个InvocationHandler类型的属性(本来开始以为是在生成的代理类中持有的,后来发现是Proxy中的,且待后面分解),实现了接口中的所有方法,然后在代理类调用某个方法的时候通过InvocationHandler去调用被代理类的方法,而在InvocationHandler类的invoke方法中嵌入了我们自己的代码,这样就实现了代理的效果,由此看来代理类和被代理类是一脉相承的,都实现了同样的接口,所以都有一样的方法,以此达到代理的目的。
我看到这里本来以为一切谜团都已经揭开,但是我突然一想,InvocationHandler里的invoke方法是需要传递一个Method类型参数的,这个是怎么传进去的,于是我继续在思考,请教别人,终于一个大师告诉我让我dump出JVM的进程里的class来,这样就可以看到内存里的$Proxy0的实现代码了,感觉他说的有道理,于是我在Linux下装了一个JDK1.7版本,开始了我的摸索之路,以下部分是引用加自己的总结:
InstanceKlass对应于HotSpot中表示Java类的内部对象。Sun JDK为反射调用生成的类的名字形如sun/reflect/GeneratedMethodAccessorN,其中N是一个整数;所以只要看看类名是否以"sun/reflect/GeneratedMethodAccessor"开头就能找出来了。留意到这里包名的分隔符是“/”而不是“.”,这是Java类在JVM中的“内部名称”形式.
现在让我前面的代码动态代码跑起来,为了不让进程很快结束,所以我加入了io阻塞等待用户输入,让进程不用结束。
然后我们通过jps命令查看所有的java进程,得到进程号。
接下来执行ClassDump,指定上面自定义的过滤器:
这样子就在当前的目录下生成了一个$Proxy0.class的文件,这个文件可以通过javap查看,但看起来比较费劲,于是我找了一个Xjad将他反编译过来了,代码如下:
以上这些对我来说也就谜底揭开了,感觉一天比较充实了。。。。。。
首先写了一个简单的动态代理:
import java.lang.reflect.*; import java.io.*; public class TestDynamicProxy{ public static void main(String[] args)throws Exception{ Hello biz = new HelloImpl(); InvocationHandler dpm = new MyInvocationHandler(biz); Hello hello = (Hello)Proxy.newProxyInstance(biz .getClass().getClassLoader(), biz.getClass().getInterfaces(), dpm); hello.sayHello(); InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); String msg = br.readLine(); System.out.println(msg); br.close(); isr.close(); } } interface Hello{ public void sayHello(); } class HelloImpl implements Hello{ public void sayHello(){ System.out.println("Hello World!"); } } class MyInvocationHandler implements InvocationHandler{ private Hello biz ; public MyInvocationHandler(Hello biz){ this.biz = biz; } public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{ System.out.println("before sayHello......."); Object o = method.invoke(biz,args); System.out.println("after sayHello.........."); return o; } }
写完这个动态代理我就开始看Proxy类的源代码,以下的方法也就成了我重点关注的,重点的代码进行了红色加粗,但这还没完:
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException { if (interfaces.length > 65535) { //一个java类最多可实现65535个接口 throw new IllegalArgumentException("interface limit exceeded"); } Class proxyClass = null; String[] interfaceNames = new String[interfaces.length]; Set interfaceSet = new HashSet(); // for detecting duplicates for (int i = 0; i < interfaces.length; i++) { String interfaceName = interfaces[i].getName(); Class interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } Object key = Arrays.asList(interfaceNames); Map cache; synchronized (loaderToCache) { cache = (Map) loaderToCache.get(loader); if (cache == null) { cache = new HashMap(); loaderToCache.put(loader, cache); } } synchronized (cache) { do { Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } if (proxyClass != null) { // proxy class already generated: return it return proxyClass; } else if (value == pendingGenerationMarker) { // proxy class being generated: wait for it try { cache.wait(); } catch (InterruptedException e) { } continue; } else { cache.put(key, pendingGenerationMarker); break; } } while (true); } try { String proxyPkg = null; // package to define proxy class in for (int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); if (!Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, proxyPkg = ""; // use the unnamed package } { long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } //proxyPkg是null,proxyClassNamePrefix是$Proxy,而num是个数字,所以生成的代理类的名称就是$Proxy0这样子的。 String proxyName = proxyPkg + proxyClassNamePrefix + num; [color=red][b]byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);[/b][/color] try { proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } // add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass, null); } finally { synchronized (cache) { if (proxyClass != null) { cache.put(key, new WeakReference(proxyClass)); } else { cache.remove(key); } cache.notifyAll(); } } return proxyClass; }
看到了这里我似乎明白了,原来动态代理是JDK通过反射机制获取了被代理对象的所有接口(动态代理只能实现接口),然后重新生成一个新的类,当然这个类没有保存成文件,而只是在内存里面,这个新的类implements了被代理对象的所有接口,同时他还持有一个InvocationHandler类型的属性(本来开始以为是在生成的代理类中持有的,后来发现是Proxy中的,且待后面分解),实现了接口中的所有方法,然后在代理类调用某个方法的时候通过InvocationHandler去调用被代理类的方法,而在InvocationHandler类的invoke方法中嵌入了我们自己的代码,这样就实现了代理的效果,由此看来代理类和被代理类是一脉相承的,都实现了同样的接口,所以都有一样的方法,以此达到代理的目的。
我看到这里本来以为一切谜团都已经揭开,但是我突然一想,InvocationHandler里的invoke方法是需要传递一个Method类型参数的,这个是怎么传进去的,于是我继续在思考,请教别人,终于一个大师告诉我让我dump出JVM的进程里的class来,这样就可以看到内存里的$Proxy0的实现代码了,感觉他说的有道理,于是我在Linux下装了一个JDK1.7版本,开始了我的摸索之路,以下部分是引用加自己的总结:
引用
SA(Serviceability Agent)自带了一个能把当前在HotSpot中加载了的类dump成Class文件的工具,称为ClassDump。它的全限定类名是sun.jvm.hotspot.tools.jcore.ClassDump,有main()方法,可以直接从命令行执行;接收一个命令行参数,是目标Java进程的进程ID,可以通过JDK自带的jps工具查找Java进程的ID。要执行该工具需要确保SA的JAR包在classpath上,位于$JAVA_HOME/lib/sa-jdi.jar。
默认条件下执行ClassDump会把当前加载的所有Java类都dump到当前目录下,如果有全限定名相同但内容不同的类同时存在于一个Java进程中,那么dump的时候会有覆盖现象,实际dump出来的是同名的类的最后一个(根据ClassDump工具的遍历顺序)。
如果需要指定被dump的类的范围,可以自己写一个过滤器,在启动ClassDump工具时指定-Dsun.jvm.hotspot.tools.jcore.filter=filterClassName,具体方法见下面例子;如果需要指定dump出来的Class文件的存放路径,可以用-Dsun.jvm.hotspot.tools.jcore.outputDir=path来指定,path替换为实际路径。
默认条件下执行ClassDump会把当前加载的所有Java类都dump到当前目录下,如果有全限定名相同但内容不同的类同时存在于一个Java进程中,那么dump的时候会有覆盖现象,实际dump出来的是同名的类的最后一个(根据ClassDump工具的遍历顺序)。
如果需要指定被dump的类的范围,可以自己写一个过滤器,在启动ClassDump工具时指定-Dsun.jvm.hotspot.tools.jcore.filter=filterClassName,具体方法见下面例子;如果需要指定dump出来的Class文件的存放路径,可以用-Dsun.jvm.hotspot.tools.jcore.outputDir=path来指定,path替换为实际路径。
import sun.jvm.hotspot.tools.jcore.ClassFilter; import sun.jvm.hotspot.oops.InstanceKlass; public class MyFilter implements ClassFilter { @Override public boolean canInclude(InstanceKlass kls) { String klassName = kls.getName().asString(); return klassName.contains("$Proxy"); } }
InstanceKlass对应于HotSpot中表示Java类的内部对象。Sun JDK为反射调用生成的类的名字形如sun/reflect/GeneratedMethodAccessorN,其中N是一个整数;所以只要看看类名是否以"sun/reflect/GeneratedMethodAccessor"开头就能找出来了。留意到这里包名的分隔符是“/”而不是“.”,这是Java类在JVM中的“内部名称”形式.
现在让我前面的代码动态代码跑起来,为了不让进程很快结束,所以我加入了io阻塞等待用户输入,让进程不用结束。
然后我们通过jps命令查看所有的java进程,得到进程号。
$ jps 20542 Demo 20554 Jps
接下来执行ClassDump,指定上面自定义的过滤器:
$ java -classpath ".:./bin:$JAVA_HOME/lib/sa-jdi.jar" -Dsun.jvm.hotspot.tools.jcore.filter=MyFilter sun.jvm.hotspot.tools.jcore.ClassDump 20542
这样子就在当前的目录下生成了一个$Proxy0.class的文件,这个文件可以通过javap查看,但看起来比较费劲,于是我找了一个Xjad将他反编译过来了,代码如下:
import java.lang.reflect.*; public final class $Proxy0 extends Proxy //继承了Proxy类 implements Hello { private static Method m3; private static Method m1; private static Method m0; private static Method m2; public final void sayHello() //实现了接口中的sayHello方法, { try { super.h.invoke(this, m3, null);//调用了InvocationHandler里的invoke方法吧 return; } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public $Proxy0(InvocationHandler invocationhandler) //生成了一个这样的构造函数 { super(invocationhandler); } //除了实现了接口中的方法外,代理类另外还重写了hashCode,equals,toString三个方法。 public final int hashCode() { try { return ((Integer)super.h.invoke(this, m0, null)).intValue(); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final boolean equals(Object obj) { try { return ((Boolean)super.h.invoke(this, m1, new Object[] { obj })).booleanValue(); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch (Error ) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } //静态代码块,初始化了几个Method属性,这里m3就是接口中的sayHello方法,如果接口中还有其他的方法他也会实现,可能会是m4,m5,m6...... 这里m3 = Class.forName("Hello").getMethod("sayHello", new Class[0]);直接以字符串常量的方式传进去,应该是在生成代理类之前通过Method对象的getName方法获取到了。 static { try { m3 = Class.forName("Hello").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("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); } catch (NoSuchMethodException nosuchmethodexception) { throw new NoSuchMethodError(nosuchmethodexception.getMessage()); } catch (ClassNotFoundException classnotfoundexception) { throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } } }
以上这些对我来说也就谜底揭开了,感觉一天比较充实了。。。。。。
发表评论
-
JDK Tools and Utilities
2013-04-11 14:32 1319Oracle官方网址:http://docs.oracle.c ... -
【转载】深入理解Java虚拟机》笔记
2013-04-10 09:59 1032【转自】http://www.admin10000.com/d ... -
Sun java命令的JVM参数收集【持续更新】
2013-04-10 09:33 3019从网络上看了很多的关 ... -
【转载】Java的内存回收机制
2013-04-10 08:52 984【转自】 http://www.admin10 ... -
读JDK源码-集合部分
2013-03-26 15:42 1401以前读过一遍JDK源码的 ... -
JAVA 字符串常量池
2012-05-15 10:59 48971. 首先String不属于8种基本数据类型,String是一 ... -
java String类的split方法
2012-04-18 17:24 1597String str = "==he=llo==& ...
相关推荐
### JDK动态代理详解 #### 一、引言 在软件工程中,代理模式是一种常见的设计模式,它通过为一个对象提供一个替代品或占位符来控制对这个对象的访问。这种模式通常用于添加额外的功能(例如日志记录、事务管理等)...
本篇文章将深入探讨JDK动态代理和CGLIB代理的区别,以及它们在实际应用中的选择。 首先,JDK动态代理主要依赖于java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。Proxy类用于创建一个代理对象...
### 关于JDK动态代理的源码剖析 #### 一、引言 在Java开发过程中,动态代理技术是一项非常实用的技术,它可以帮助我们实现在不修改原有代码的基础上为方法增加额外的功能,比如日志记录、权限校验等。本文将深入...
Spring 框架中 JDK 动态代理和 CGLIB 动态代理是 Spring AOP 中一个非常重要的知识点。Spring AOP 框架会根据实际情况选择使用 JDK 的动态代理还是 CGLIB 的动态代理。 JDK 动态代理是 Java 自带的动态代理机制,它...
JDK 动态代理技术详解 JDK 动态代理技术是 Java 语言自身对动态代理的支持,类似于 JDK 中在 java.util 包中提供 Observable 类和 Observer 接口提供对观察者模式的语言级支持。动态代理的优点是可以动态地为软件...
在Java编程领域,JDK动态代理是一个非常重要的概念,它允许我们在运行时动态地创建一个实现了特定接口的代理对象,以此来拦截并扩展原有对象的行为。动态代理在很多场景下都有应用,比如AOP(面向切面编程)、事件...
JDK动态代理是Java编程中一个非常重要的特性,它允许我们在运行时创建具有特定接口的代理类实例。这种技术在很多场景下都非常有用,比如在AOP(面向切面编程)中实现方法拦截、日志记录、事务管理等。下面我们将深入...
在Java编程领域,JDK动态代理是实现动态创建代理对象的一种技术,它是Java标准库提供的一种强大工具。Spring AOP(面向切面编程)则是一种流行的应用框架,它利用动态代理来实现对业务代码的切面增强,如日志、事务...
Spring框架是AOP实现的一个典范,它提供了两种主要的动态代理方式:JDK动态代理和CGLib动态代理。 **JDK动态代理**: JDK动态代理基于Java的反射API实现,适用于接口代理。当目标对象实现了至少一个接口时,Spring...
JDK动态代理和CGlib动态代理是Java中实现这一目标的两种主要方式。 ### JDK动态代理 JDK动态代理基于Java的接口实现。如果一个类实现了至少一个接口,我们就可以为这个类创建一个动态代理。动态代理通过`java.lang....
jdk 的动态代理和CGLIB代理
本文将深入探讨两种主要的Java代理实现:JDK动态代理和CGLIB代理。 一、JDK动态代理 JDK动态代理基于接口实现,它要求被代理的类必须实现至少一个接口。在运行时,Java会动态地创建一个新的类,这个类实现了与原始...
在Java中,我们可以使用JDK的动态代理或者Spring AOP来实现代理模式。 JDK动态代理主要依赖于`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口。Proxy类是生成代理对象的工厂,而...
JDK动态代理和CGLIB代理是两种常用的实现方式。 首先,我们来看看JDK动态代理。JDK动态代理主要通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口来实现。Proxy类用于创建一个代理对象...
- **CGLIB代理**适用于目标类没有接口或者不希望修改原有接口的情况,其性能通常优于JDK代理,因为它是基于字节码生成的子类,而JDK代理需要反射调用接口方法。 在实际开发中,如Spring AOP框架就同时支持JDK和...
Spring AOP允许我们通过代理来实现横切关注点,如日志、事务管理等,而JDK动态代理则是Spring AOP实现的一种方式。本文将深入探讨Spring如何利用JDK动态代理技术来实现这一功能,并通过实例解析其底层实现。 首先,...
在本文中,我们将深入探讨如何模拟JDK的动态代理内部实现。 首先,我们需要了解JDK动态代理的基础知识。Java中的动态代理通过`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口实现。`Proxy...
Java JDK 动态代理是一种强大的特性,它允许我们在运行时创建代理对象,这些代理对象可以扩展或增强已存在的接口实现。动态代理在处理AOP(面向切面编程)场景、事件监听、性能监控等方面有着广泛的应用。下面我们将...
CGLIB和JDK动态代理是两种常用的实现方式,它们各有优缺点,适用于不同的场景。下面将详细探讨这两种动态代理的区别。 首先,JDK动态代理主要依赖于`java.lang.reflect.Proxy`类和`java.lang.reflect....