java proxy 动态代理实现原理与用法
2017-10-05 17:19
204 查看
在学习动态代理之前我们需要大致的了解一下代码模式与装饰器模式
代理模式是为了提供对象的访问控制。
装饰者模式是为了实现对象对象功能的增强。
他们的主要区别如下:
使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。
详情可查考:http://www.cnblogs.com/jaredlam/archive/2011/11/08/2241089.html
那么动态代理和代理模式有什么区别呢:动态代理就是利用反射和动态编译将代理模式变成动态的。原理跟动态注入一样,代理模式在编译的时候就已经确定代理类将要代理谁,而动态代理在运行的时候才知道自己要代理谁。
在学习使用JDK动态代理之前我们先来尝试着自己完成动态代理的简单实现
1.首先定义一个代理对象接口
现在定义一个要被代理的接口类(使用JDK的动态代理,需要被代理对象实现一个接口,至于原因接下来看就可以明白了)
该代理对象的作用就是获取被代理对象的class,然后根据class利用反射生成源码,并加载到内存中,在通过构造器实例化对象返回代理对象。
现在代理对象已经完成,接下来就可以使用了,如下客户端调用
开启事务.....
添加用户
提交事务.....
-------------------------------
开启事务.....
删除用户
提交事务.....
现在是是时候解释为何是接口而不能传入类了。
在代理对象实现中是通过反射实现了,里面有如下的代码
如果我们传入的是接口,那么此方法就能够清楚而明确的获取到我们想要代理的方法,也就是仅仅出现在接口中的方法定义,但是如果我们传入的是类,会发生什么了,由于
Method[] methods=infce.getMethods();会获取到本类以及父类的所有方法,因而会引入许多我们原本不希望被代理的方法,下面我们看看在代理类中我们传入接口以及传入类生成的源码:
传入接口生成的源码
同时控制台的输出如下:
通过以上的讲解,相信大家已经明白了动态代理的实现原理,以及为何要求被代理类实现接口的原因了
注:以上文章参考了 http://blog.csdn.net/liushuijinger/article/details/37829049 该文章写的十分不错。
动态代理的显著好处,就是所有的类,都可以共用系统中提供的公共服务,诸如例子中提到的事务管理公共服务
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
了解了动态代理与静态代理的区别以及实现原理之后,我们再来学习JDK中提供的动态代理的用法,就比较简单了。
使用JDK动态代理的5个步骤
1.通过实现InvocationHandler接口来定义自己的InvocationHandler;(通过上面的学习我们可以知道该实现类决定了我们动态代理实际增加的功能)
2.通过Proxy.getProxyClass获得动态代理类;(实际上就是利用JDK帮助我们自动生成代理对象加载到内存)
3.通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class) ;实际上就是通过反射获取构造方法
4.通过构造函数获得代理对象并将定义的InvocationHandler实例对象作为参数传入 ;实际上就是注入代理对象的委托类
5.通过代理对象调用目标方法; 实际上就是在InvocationHandler实现类中执行方法
例子:
但是通过上面标准的方法代码量比较大,JDK帮我们进行了2-4步骤的封装
其方法签名为:
如下例:
JDK 代理生成器,在生成类是会根据参数“sun.misc.ProxyGenerator.saveGeneratedFiles”来决定是否将二进制保存到本地文件中
首先我看看把其设置为false,项目中都生成了哪些class文件
MyProxy$Hello.class
MyProxy$IHello.class
MyProxy$InvocationHandlerImpl.class
MyProxy.class
再来看看我们将其设置为true项目中都会生成哪些class文件
MyProxy$Hello.class
MyProxy$IHello.class
MyProxy$InvocationHandlerImpl.class
MyProxy.class
发现生成的class文件一样,而且在我家项目的class文件删除后,在将其设置为true,项目运行的时候就会报错,而设置为false,却不会报错能正常生成class文件并执行。其中的原因,以及何时应该设置saveGeneratedFiles参数为true在一般博客里面看到其介绍说: 当项目比较大,或者使用的框架比较多时,千万不要将saveGeneratedFiles设置为true容易导致内存泄露。不知道是否正确。
通过上面的解析相信大家对于JDK代理如何使用应该有了比较清楚的认识。
最后我们再来看看JDK中的Proxy的实现原理
接下来我们在看看是如何获取代理类的
我们再来看看创建代理类的实现代码
最后我们看看字节码的生成方法
在动态代理中InvocationHandler是核心,每个代理实例都具有一个关联的调用处理程序(InvocationHandler)。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序(InvocationHandler)的 invoke 方法。所以对代理方法的调用都是通InvocationHadler的invoke来实现中,而invoke方法根据传入的代理对象,方法和参数来决定调用代理的哪个方法
invoke方法签名:invoke(Object Proxy,Method method,Object[] args)
最后再来看看上例子中生成的$Proxy0.class是如何的:该类在com.sun.proxy中,在编辑器中如下:
看上上述实现之后,对于之前的关于
代理模式是为了提供对象的访问控制。
装饰者模式是为了实现对象对象功能的增强。
他们的主要区别如下:
使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。
详情可查考:http://www.cnblogs.com/jaredlam/archive/2011/11/08/2241089.html
那么动态代理和代理模式有什么区别呢:动态代理就是利用反射和动态编译将代理模式变成动态的。原理跟动态注入一样,代理模式在编译的时候就已经确定代理类将要代理谁,而动态代理在运行的时候才知道自己要代理谁。
在学习使用JDK动态代理之前我们先来尝试着自己完成动态代理的简单实现
1.首先定义一个代理对象接口
package Proxys; import java.lang.reflect.Method; public interface InvocationHandler { public void invoke(Object o,Method method); }现在模式实现一个事务管理对象(也就是需要对被代理的对象增加的处理类)
package Proxys; import java.lang.reflect.Method; /** * Created by fangiming on 2017/10/5. */ public class TransactionHandler implements InvocationHandler { private Object target; public TransactionHandler(Object target) { super(); this.target = target; } @Override public void invoke(Object o, Method m) { System.out.println("开启事务....."); try { m.invoke(target); } catch (Exception e) { e.printStackTrace(); } System.out.println("提交事务....."); } }
现在定义一个要被代理的接口类(使用JDK的动态代理,需要被代理对象实现一个接口,至于原因接下来看就可以明白了)
package Proxys; public interface UserMgr { void addUser(); void delUser(); }然后定义一个该接口的实现类
package Proxys; public class UserMgrImpl implements UserMgr{ @Override public void addUser() { System.out.println("添加用户"); } @Override public void delUser() { System.out.println("删除用户"); } }以上准备工作做好了之后,我们就可以开始实现代理对象了
package Proxys; import javax.tools.JavaCompiler; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; /** * Created by fangiming on 2017/10/5. */ public class Proxy { public static Object newProxyInstance(Class infce,InvocationHandler h)throws Exception{ String methodStr=""; String rt="\r\n"; //利用反射得到infce的所有方法,并重新组装 Method[] methods=infce.getMethods(); for(Method m : methods){ methodStr += " @Override" + rt + " public "+m.getReturnType()+" " + m.getName() + "() {" + rt + " try {" + rt + " Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt + " h.invoke(this, md);" + rt + //实际做事的还是委托给了 InvocationHandler " }catch(Exception e) {e.printStackTrace();}" + rt + " }" + rt ; } //生成代理对象的源码 String srcCode = "package Proxys;" + rt + "import java.lang.reflect.Method;" + rt + "public class $Proxy1 implements " + infce.getName() + "{" + rt + " public $Proxy1(InvocationHandler h) {" + rt + " this.h = h;" + rt + " }" + rt + " Proxys.InvocationHandler h;" + rt + //增强功能的实现类 methodStr + rt + //方法 "}"; //定义该源码写入的路径 String fileName="D:\\jeecg37\\sort\\out\\production\\sort\\Proxys\\$Proxy1.java"; File f=new File(fileName); FileWriter fw=new FileWriter(f); fw.write(srcCode); fw.flush(); fw.close(); //将java文件编译成class文件 JavaCompiler compiler= ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager=compiler.getStandardFileManager(null,null,null); Iterable units=fileManager.getJavaFileObjects(fileName); JavaCompiler.CompilationTask t = compiler.getTask(null, fileManager, null, null, null, units); t.call(); fileManager.close(); //加载到内存,并实例化 URL[] urls=new URL[]{new URL("file:/"+"D:/jeecg37/sort/out/production/sort/")}; URLClassLoader ul=new URLClassLoader(urls); Class c=ul.loadClass("Proxys.$Proxy1"); Constructor ctr=c.getConstructor(InvocationHandler.class); Object m=ctr.newInstance(h); //通过构造器对象实例化该对象并返回 return m; } }
该代理对象的作用就是获取被代理对象的class,然后根据class利用反射生成源码,并加载到内存中,在通过构造器实例化对象返回代理对象。
现在代理对象已经完成,接下来就可以使用了,如下客户端调用
package Proxys; public class Client { public static void main(String[] args)throws Exception { UserMgr mgr=new UserMgrImpl(); InvocationHandler h = new TransactionHandler(mgr); //将目标对象注入到代理类中 UserMgr u = (UserMgr)Proxy.newProxyInstance(UserMgr.class,h); // UserMgrImpl mgr=new UserMgrImpl(); // InvocationHandler h = new TransactionHandler(mgr); // UserMgr u = (UserMgr)Proxy.newProxyInstance(UserMgrImpl.class,h); u.addUser(); System.out.println("-------------------------------"); u.delUser(); } }下面我们看看输出
开启事务.....
添加用户
提交事务.....
-------------------------------
开启事务.....
删除用户
提交事务.....
现在是是时候解释为何是接口而不能传入类了。
在代理对象实现中是通过反射实现了,里面有如下的代码
Method[] methods=infce.getMethods();
如果我们传入的是接口,那么此方法就能够清楚而明确的获取到我们想要代理的方法,也就是仅仅出现在接口中的方法定义,但是如果我们传入的是类,会发生什么了,由于
Method[] methods=infce.getMethods();会获取到本类以及父类的所有方法,因而会引入许多我们原本不希望被代理的方法,下面我们看看在代理类中我们传入接口以及传入类生成的源码:
传入接口生成的源码
package Proxys; import java.lang.reflect.Method; public class $Proxy1 implements Proxys.UserMgr{ public $Proxy1(InvocationHandler h) { this.h = h; } Proxys.InvocationHandler h; @Override public void delUser() { try { Method md = Proxys.UserMgr.class.getMethod("delUser"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } @Override public void addUser() { try { Method md = Proxys.UserMgr.class.getMethod("addUser"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } }传入类生成的源码:
package Proxys; import java.lang.reflect.Method; public class $Proxy1 implements Proxys.UserMgrImpl{ public $Proxy1(InvocationHandler h) { this.h = h; } Proxys.InvocationHandler h; @Override public void delUser() { try { Method md = Proxys.UserMgrImpl.class.getMethod("delUser"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } @Override public void addUser() { try { Method md = Proxys.UserMgrImpl.class.getMethod("addUser"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } @Override public void wait() { try { Method md = Proxys.UserMgrImpl.class.getMethod("wait"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } @Override public void wait() { try { Method md = Proxys.UserMgrImpl.class.getMethod("wait"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } @Override public void wait() { try { Method md = Proxys.UserMgrImpl.class.getMethod("wait"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } @Override public boolean equals() { try { Method md = Proxys.UserMgrImpl.class.getMethod("equals"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } @Override public class java.lang.String toString() { try { Method md = Proxys.UserMgrImpl.class.getMethod("toString"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } @Override public int hashCode() { try { Method md = Proxys.UserMgrImpl.class.getMethod("hashCode"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } @Override public class java.lang.Class getClass() { try { Method md = Proxys.UserMgrImpl.class.getMethod("getClass"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } @Override public void notify() { try { Method md = Proxys.UserMgrImpl.class.getMethod("notify"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } @Override public void notifyAll() { try { Method md = Proxys.UserMgrImpl.class.getMethod("notifyAll"); h.invoke(this, md); }catch(Exception e) {e.printStackTrace();} } }
同时控制台的输出如下:
D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:51: 错误: 需要'{' public class java.lang.String toString() { ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:52: 错误: 非法的类型开始 try { ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:52: 错误: 需要';' try { ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:54: 错误: 需要<标识符> h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:54: 错误: 非法的类型开始 h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:54: 错误: 需要<标识符> h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:55: 错误: 非法的类型开始 }catch(Exception e) {e.printStackTrace();} ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:58: 错误: 需要class, interface或enum public int hashCode() { ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:60: 错误: 需要<标识符> Method md = Proxys.UserMgrImpl.class.getMethod("hashCode"); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:60: 错误: 方法声明无效; 需要返回类型 Method md = Proxys.UserMgrImpl.class.getMethod("hashCode"); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:60: 错误: 非法的类型开始 Method md = Proxys.UserMgrImpl.class.getMethod("hashCode"); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:61: 错误: 需要<标识符> h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:61: 错误: 非法的类型开始 h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:61: 错误: 需要<标识符> h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:62: 错误: 需要class, interface或enum }catch(Exception e) {e.printStackTrace();} ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:62: 错误: 需要class, interface或enum }catch(Exception e) {e.printStackTrace();} ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:65: 错误: 需要'{' public class java.lang.Class getClass() { ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:66: 错误: 非法的类型开始 try { ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:66: 错误: 需要';' try { ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:68: 错误: 需要<标识符> h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:68: 错误: 非法的类型开始 h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:68: 错误: 需要<标识符> h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:69: 错误: 需要class, interface或enum }catch(Exception e) {e.printStackTrace();} ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:69: 错误: 需要class, interface或enum }catch(Exception e) {e.printStackTrace();} ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:72: 错误: 需要class, interface或enum public void notify() { ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:74: 错误: 需要<标识符> Method md = Proxys.UserMgrImpl.class.getMethod("notify"); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:74: 错误: 方法声明无效; 需要返回类型 Method md = Proxys.UserMgrImpl.class.getMethod("notify"); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:74: 错误: 非法的类型开始 Method md = Proxys.UserMgrImpl.class.getMethod("notify"); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:75: 错误: 需要<标识符> h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:75: 错误: 非法的类型开始 h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:75: 错误: 需要<标识符> h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:76: 错误: 需要class, interface或enum }catch(Exception e) {e.printStackTrace();} ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:76: 错误: 需要class, interface或enum }catch(Exception e) {e.printStackTrace();} ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:79: 错误: 需要class, interface或enum public void notifyAll() { ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:81: 错误: 需要<标识符> Method md = Proxys.UserMgrImpl.class.getMethod("notifyAll"); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:81: 错误: 方法声明无效; 需要返回类型 Method md = Proxys.UserMgrImpl.class.getMethod("notifyAll"); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:81: 错误: 非法的类型开始 Method md = Proxys.UserMgrImpl.class.getMethod("notifyAll"); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:82: 错误: 需要<标识符> h.invoke(this, md); ^来接 D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:82: 错误: 非法的类型开始 h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:82: 错误: 需要<标识符> 10e63 h.invoke(this, md); ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:83: 错误: 需要class, interface或enum }catch(Exception e) {e.printStackTrace();} ^ D:\jeecg37\sort\out\production\sort\Proxys\$Proxy1.java:83: 错误: 需要class, interface或enum }catch(Exception e) {e.printStackTrace();} ^ 42 个错误 开启事务..... 添加用户 提交事务..... ------------------------------- 开启事务..... 删除用户 提交事务.....
通过以上的讲解,相信大家已经明白了动态代理的实现原理,以及为何要求被代理类实现接口的原因了
注:以上文章参考了 http://blog.csdn.net/liushuijinger/article/details/37829049 该文章写的十分不错。
动态代理的显著好处,就是所有的类,都可以共用系统中提供的公共服务,诸如例子中提到的事务管理公共服务
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
了解了动态代理与静态代理的区别以及实现原理之后,我们再来学习JDK中提供的动态代理的用法,就比较简单了。
使用JDK动态代理的5个步骤
1.通过实现InvocationHandler接口来定义自己的InvocationHandler;(通过上面的学习我们可以知道该实现类决定了我们动态代理实际增加的功能)
2.通过Proxy.getProxyClass获得动态代理类;(实际上就是利用JDK帮助我们自动生成代理对象加载到内存)
3.通过反射机制获得代理类的构造方法,方法签名为getConstructor(InvocationHandler.class) ;实际上就是通过反射获取构造方法
4.通过构造函数获得代理对象并将定义的InvocationHandler实例对象作为参数传入 ;实际上就是注入代理对象的委托类
5.通过代理对象调用目标方法; 实际上就是在InvocationHandler实现类中执行方法
例子:
package Proxys; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * Created by fangiming on 2017/10/5. */ public class MyProxy { //被代理类的接口 public interface IHello{ void sayHello(); } //被代理类的实现类 static class Hello implements IHello{ @Override public void sayHello() { System.out.println("Hello World!"); } } //代理类的增强方法实现类 static class InvocationHandlerImpl implements InvocationHandler { private Object target; //被代理对象的引用 InvocationHandlerImpl(Object target){ //构造方法 this.target=target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("-----------befor--------------"); Object result=method.invoke(target,args); System.out.println("-----------after--------------"); return result; } } //测试 public static void main(String[] args)throws Exception{ //生成$Proxy0的class文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); //获取动态代理类 (参数:类加载器+接口) Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(), IHello.class); //获得代理类的构造函数,并传入参数类型InvocationHandler.class Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class); //通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入 IHello iHello = (IHello) constructor.newInstance(new InvocationHandlerImpl(new Hello())); //通过代理对象调用目标方法 iHello.sayHello(); } }
但是通过上面标准的方法代码量比较大,JDK帮我们进行了2-4步骤的封装
其方法签名为:
newProxyInstance(ClassLoader loader,Class<?>[] instance, InvocationHandler h)
如下例:
//测试 public static void main(String[] args)throws Exception{ // //生成$Proxy0的class文件 // System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); // //获取动态代理类 (参数:类加载器+接口) // Class proxyClazz = Proxy.getProxyClass(IHello.class.getClassLoader(), IHello.class); // //获得代理类的构造函数,并传入参数类型InvocationHandler.class // Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class); // //通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入 // IHello iHello = (IHello) constructor.newInstance(new InvocationHandlerImpl(new Hello())); // //通过代理对象调用目标方法 // iHello.sayHello(); //生成$Proxy0的class文件 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); IHello ihello = (IHello) Proxy.newProxyInstance(IHello.class.getClassLoader(), //加载接口的类加载器 new Class[]{IHello.class}, //一组接口 new InvocationHandlerImpl(new Hello())); //自定义的InvocationHandler ihello.sayHello(); }通过测试我们发现我们即使将
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");去掉,仍旧能够获取我们需要的结果,首先我们看看其作用
JDK 代理生成器,在生成类是会根据参数“sun.misc.ProxyGenerator.saveGeneratedFiles”来决定是否将二进制保存到本地文件中
首先我看看把其设置为false,项目中都生成了哪些class文件
MyProxy$Hello.class
MyProxy$IHello.class
MyProxy$InvocationHandlerImpl.class
MyProxy.class
再来看看我们将其设置为true项目中都会生成哪些class文件
MyProxy$Hello.class
MyProxy$IHello.class
MyProxy$InvocationHandlerImpl.class
MyProxy.class
发现生成的class文件一样,而且在我家项目的class文件删除后,在将其设置为true,项目运行的时候就会报错,而设置为false,却不会报错能正常生成class文件并执行。其中的原因,以及何时应该设置saveGeneratedFiles参数为true在一般博客里面看到其介绍说: 当项目比较大,或者使用的框架比较多时,千万不要将saveGeneratedFiles设置为true容易导致内存泄露。不知道是否正确。
通过上面的解析相信大家对于JDK代理如何使用应该有了比较清楚的认识。
最后我们再来看看JDK中的Proxy的实现原理
@CallerSensitive public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); //InvocationHandler非空 final Class<?>[] intfs = interfaces.clone(); //对接口对象进行拷贝 final SecurityManager sm = System.getSecurityManager(); //安全性检查 if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } //确保产生代理类 Class<?> cl = getProxyClass0(loader, intfs); try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //获取代理类的构造函数对象 参数constructorParames为常量值:private static final Class<?>[] constructorParams = { InvocationHandler.class }; final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); //暴力访问 return null; } }); } //根据代理类的构造函数对象来创建代理类对象 return cons.newInstance(new Object[]{h}); }
接下来我们在看看是如何获取代理类的
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { ////接口数不得超过65535个 throw new IllegalArgumentException("interface limit exceeded"); } //代理类缓存,如果缓存中有代理类了直接返回,否则将由ProxyClassFactory创建代理类 // private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); return proxyClassCache.get(loader, interfaces); }
我们再来看看创建代理类的实现代码
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { //统一代理类的前缀名都以$Proxy开关 private static final String proxyClassNamePrefix = "$Proxy"; //使用唯一的编号给作为代理类名的一部分,如$Proxy0,$Proxy1等 private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { //验证指定的类加载器(loader)加载接口所得到的Class对象(interfaceClass)是否与intf对象相同 Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } //验证该Class对象是不是接口 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } //验证该接口是否重复了 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } //声明代理类所在包 String proxyPkg = null; int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /** * 验证你传入的接口中是否有非public接口,只要有一个接口是非public的,那么这些接口都必须在同一包中 * 这里的接口修饰符直接影响到System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true") * 所生成的代理类的路径,往下看!! */ for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.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) { /** * 如果都是public接口,那么生成的代理类就在com.sun.proxy包下 * 如果报java.io.FileNotFoundException: com\sun\proxy\$Proxy0.class (系统找不到指定的路径。)的错误, * 就先在你项目中创建com.sun.proxy路径 */ proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } //将当前nextUniqueNumber的值以原子的方式的加1,所以第一次生成代理类的名字为$Proxy0.class long num = nextUniqueNumber.getAndIncrement(); //代理类的完全限定名,如com.sun.proxy.$Proxy0.calss, String proxyName = proxyPkg + proxyClassNamePrefix + num; //生成代理类字节码文件 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } }这时生成字节码的方法又委托给了
ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);我们在来看看其定义
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); //生成代理类字节码文件的真正方法 final byte[] var4 = var3.generateClassFile(); /** * 保存文件:此处的 saveGeneratedFiles 即使之前定义的saveGeneratedFiles * System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); */ if(saveGeneratedFiles) { AccessController.doPrivileged(new PrivilegedAction() { public Void run() { try { int var1 = var0.lastIndexOf(46); Path var2; if (var1 > 0) { Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar), new String[0]); Files.createDirectories(var3, new FileAttribute[0]); var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class"); } else { var2 = Paths.get(var0 + ".class", new String[0]); } Files.write(var2, var4, new OpenOption[0]); return null; } catch (IOException var4x) { throw new InternalError("I/O exception saving generated file: " + var4x); } } }); } return var4; }
最后我们看看字节码的生成方法
private byte[] generateClassFile() { /** * addProxyMethod系列方法就是将接口的方法和Object的hashCode,equals,toString方法添加到代理方法容器(proxyMethods), * 其中方法签名作为key,proxyMethod作为value * * hashCodeMethod方法位于静态代码块中通过Object对象获得,hashCodeMethod=Object.class.getMethod("hashCode",new Class[0]), * 相当于从Object中继承过来了这三个方法equalsMethod,toStringMethod */ this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); //待会再来看看此方法 Class[] var1 = this.interfaces; int var2 = var1.length; int var3; Class var4; //获得所有接口中的所有方法,并将方法添加到代理方法中 for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; Method[] var5 = var4.getMethods(); //获得接口方法 int var6 = var5.length; for(int var7 = 0; var7 < var6; ++var7) { Method var8 = var5[var7]; this.addProxyMethod(var8, var4); } } Iterator var11 = this.proxyMethods.values().iterator(); List var12; while(var11.hasNext()) { var12 = (List)var11.next(); //验证具有相同方法签名的的方法的返回值类型是否一致,因为不可能有两个方法名相同,参数相同,而返回值却不同的方法 checkReturnTypes(var12); } //接下来就是写代理类文件的步骤了 Iterator var15; try { //生成代理类的构造函数 this.methods.add(this.generateConstructor()); var11 = this.proxyMethods.values().iterator(); while(var11.hasNext()) { var12 = (List)var11.next(); var15 = var12.iterator(); while(var15.hasNext()) { ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next(); /** * 将代理字段声明为Method,10为ACC_PRIVATE和ACC_STATAIC的与运算,表示该字段的修饰符为private static * 所以代理类的字段都是private static Method XXX */ this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10)); //生成代理类的代理方法 this.methods.add(var16.generateMethod()); } } //为代理类生成静态代码块,对一些字段进行初始化 this.methods.add(this.generateStaticInitializer()); } catch (IOException var10) { throw new InternalError("unexpected I/O Exception", var10); } //代理方法超过65535将抛出异常 if(this.methods.size() > '\uffff') { throw new IllegalArgumentException("method limit exceeded"); } else if(this.fields.size() > '\uffff') { //代理类的字段超过65535将抛出异常 throw new IllegalArgumentException("field limit exceeded"); } else { //这里开始就是一些代理类文件的过程,此过程略过 this.cp.getClass(dotToSlash(this.className)); this.cp.getClass("java/lang/reflect/Proxy"); var1 = this.interfaces; var2 = var1.length; for(var3 = 0; var3 < var2; ++var3) { var4 = var1[var3]; this.cp.getClass(dotToSlash(var4.getName())); } this.cp.setReadOnly(); ByteArrayOutputStream var13 = new ByteArrayOutputStream(); DataOutputStream var14 = new DataOutputStream(var13); try { var14.writeInt(-889275714); var14.writeShort(0); var14.writeShort(49); this.cp.write(var14); var14.writeShort(this.accessFlags); var14.writeShort(this.cp.getClass(dotToSlash(this.className))); var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy")); var14.writeShort(this.interfaces.length); Class[] var17 = this.interfaces; int var18 = var17.length; for(int var19 = 0; var19 < var18; ++var19) { Class var22 = var17[var19]; var14.writeShort(this.cp.getClass(dotToSlash(var22.getName()))); } var14.writeShort(this.fields.size()); var15 = this.fields.iterator(); while(var15.hasNext()) { ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next(); var20.write(var14); } var14.writeShort(this.methods.size()); var15 = this.methods.iterator(); while(var15.hasNext()) { ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next(); var21.write(var14); } var14.writeShort(0); return var13.toByteArray(); } catch (IOException var9) { throw new InternalError("unexpected I/O Exception", var9); } } }最后再来看看addProxyMethod方法
private void addProxyMethod(Method var1, Class<?> var2) { String var3 = var1.getName(); //方法名 Class[] var4 = var1.getParameterTypes(); //方法参数类型数组 Class var5 = var1.getReturnType(); //返回值类型 Class[] var6 = var1.getExceptionTypes(); //异常类型 String var7 = var3 + getParameterDescriptors(var4); //方法签名 Object var8 = (List)this.proxyMethods.get(var7); //根据方法签名却获得proxyMethods的Value if(var8 != null) { //处理多个代理接口中重复的方法的情况 Iterator var9 = ((List)var8).iterator(); while(var9.hasNext()) { ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next(); if(var5 == var10.returnType) { /** * 归约异常类型以至于让重写的方法抛出合适的异常类型,我认为这里可能是多个接口中有相同的方法, * 而这些相同的方法抛出的异常类型又不同,所以对这些相同方法抛出的异常进行了归约 */ ArrayList var11 = new ArrayList(); collectCompatibleTypes(var6, var10.exceptionTypes, var11); collectCompatibleTypes(var10.exceptionTypes, var6, var11); var10.exceptionTypes = new Class[var11.size()]; //将ArrayList转换为Class对象数组 var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes); return; } } } else { //如果var8为空,就创建一个数组,并以方法签名为key,proxymethod对象数组为value添加到proxyMethods var8 = new ArrayList(3); this.proxyMethods.put(var7, var8); } ((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2, null)); }
在动态代理中InvocationHandler是核心,每个代理实例都具有一个关联的调用处理程序(InvocationHandler)。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序(InvocationHandler)的 invoke 方法。所以对代理方法的调用都是通InvocationHadler的invoke来实现中,而invoke方法根据传入的代理对象,方法和参数来决定调用代理的哪个方法
invoke方法签名:invoke(Object Proxy,Method method,Object[] args)
最后再来看看上例子中生成的$Proxy0.class是如何的:该类在com.sun.proxy中,在编辑器中如下:
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.sun.proxy; import Proxys.MyProxy.IHello; 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 IHello { 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 { try { return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue(); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void sayHello() 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 { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue(); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")}); m3 = Class.forName("Proxys.MyProxy$IHello").getMethod("sayHello", 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]); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }以上文章主要参考 http://www.cnblogs.com/MOBIN/p/5597215.html 写的也很nice
看上上述实现之后,对于之前的关于
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "false");实际的作用就会更明白了,当时找的路径不对,因而代理类生成的对象在com.sun.proxy中,当其为true时,会针对每一个代理类生成对应的class文件并加载到内存中去,下次在访问的时候,就不用再次生成class文件,以及类加载了,直接使用就可以了,但是,如果为false,那么就会每次使用的时候都会生成class文件,执行内存加载class字节码,相对而言速度就会慢一些,但是会节省内存一点。
相关文章推荐
- JDK动态代理实现原理
- 深入剖析JDK动态代理实现原理
- Java之美[从菜鸟到高手演练]之JDK动态代理的实现及原理
- jdk动态代理的实现原理
- Java 动态代理实现及原理
- JDK动态代理实现原理
- CGLib动态代理原理及实现
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 细说JDK动态代理的实现原理
- JDK动态代理实现原理(转)
- 动态代理实现原理
- java动态代理实现原理
- Spring AOP动态代理原理与实现方式
- SpringAOP的CGLIB动态代理的底层原理实现
- jdk动态代理实现原理
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- 反射实现AOP动态代理模式(Spring AOP实现原理)
- JDK动态代理实现原理
- 反射实现 AOP 动态代理模式(Spring AOP 的实现 原理)
- JDK动态代理的实现和原理解析(基于JDK1.7)