java 动态代理原理

bestzzz666

贡献于2015-11-16

字数:9861 关键词: Java开发

作者:竹竿,QQ764714258 欢迎大家多多交流,引用请注明出处尊重原创! 阅读此文,必须首先理解Java反射机制! 下面以一个简单的银行账户为例讲述讲述动态代理。 设计一个银行账户类,包含用户的账户余额,实现查询和更新余额功能 package com; public interface BankAccount { public double queryCount(); public void updateCount(double i); } package com; public class BankAccountImpl implements BankAccount { String name=""; //户主 double count=0; //账户余额 BankAccountImpl(String name) { this.name=name; } public double queryCount() { // TODO Auto-generated method stub System.out.println(count); return count; } public void updateCount(double i) { // TODO Auto-generated method stub System.out.println("账户变化"+i); count = count+i; } } public class TestCount {       public static void main(String[] args) {           BankAccount san = new BankAccountImpl (“张三”);           san.updateCount();           san.queryCount();       }   } 这个系统用了一段时间,有客户要求对账说账户余额给弄错了?因为上面没有存取款记录,最后银行不认账,客户收到了损失。银行为了避免这种现象再次发生,决定对这个系统进行修改,但是因为bankAccount太过复杂,希望在不修改bankAccount的情况下,增加日志功 能。 静态代理 使用静态代理解决上面的问题。 public class BankAccountStaticProxy implements BankAccount { public bankAccountImpl impl; BankAccountStaticProxy (BankAccountImpl object){ impl=object; } public double queryCount(){ System.out.println("某天某月某日某时某分查询余额,余额为%d",count); impl. queryCount(); } public void updateCount(double i){ if(i<0) System.out.println("某天某月某日某时某分取款,取款额度为%d",i); If(i>0) System.out.println("某天某月某日某时某分存款,存款额度为%d",i); impl.updateCount(i); } } public class TestCount {        public static void main(String[] args) {             BankAccount san = new BankAccountImpl (“张三”);    BankAccount sanProxy = BankAccountStaticProxy (san);           sanProxy.updateCount();             sanProxy.queryCount();        }   } 银行要求所有模块都需要添加日志功能,这对苦逼的程序员来说真的是一个不小的工作量啊,这需要写多少个自定义的代理类???还有没有时间可以愉快的玩耍了! 动态代理 Java JDK提供了一种动态代理实现机制,不用为每一个类自己手动去编写一个代理类,它可以帮你自动生成代理类。 下面讲一下,上面用到的几个关键方法和接口: InvocationHandler接口:  public interface InvocationHandler {  public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;  }  参数说明:  Object proxy:动态生成的代理类的实例 Method method:要调用的方法  Object[] args:方法调用时所需要的参数  可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。  Proxy类:  Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:  public static Object newProxyInstance(ClassLoader loader, Class[] interfaces,  InvocationHandler h)                                 throws IllegalArgumentException  参数说明:  ClassLoader loader:类加载器  Class[] interfaces:得到全部的接口  InvocationHandler h:得到InvocationHandler接口的子类实例  Ps:类加载器  在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器;  Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的;  Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类;  AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。  package com; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactory implements InvocationHandler { private Object target; /** * 绑定委托对象并返回一个代理类 * @param target * @return */ public Object bind(Object target) { this.target = target; //取得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷) } /* * 第一个参数,是动态生成的代理类的对象。 * 第二个参数,是调用的方法 * 第三个参数,是传递的参数 * */ public Object invoke(Object obj, Method method, Object[] arg2) throws Throwable { // TODO Auto-generated method stub Object result=null; System.out.println("在这里添加你要添加的日志"); // 执行方法 result = method.invoke(target, arg2); System.out.println("在这里也可以添加你要添加的日志"); return result; } } package com; import sun.misc.ProxyGenerator; import java.io.FileOutputStream; public class TestCount { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub BankAccount san = new BankAccountImpl ("张三"); ProxyFactory factory= new ProxyFactory(); BankAccount sanProxy = (BankAccount)factory .bind(san); sanProxy.updateCount(10); sanProxy.queryCount(); //查看自动生成的代理类 createProxyClassFile(); } public static void createProxyClassFile() { String name = "ProxyBankAccount"; byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { BankAccount.class } ); try { FileOutputStream out = new FileOutputStream( name + ".class" ); out.write( data ); out.close(); } catch( Exception e ) { e.printStackTrace(); } } } bind() 通过Proxy的newProxyInstance创建了一个代理类对象。 通过ProxyFactory 我们可以看出来,无论bind传递任何类,bind都会帮你生成一个代理类。就可以很快的满足银行每个模块都添加日志的要求? 如果你不求甚解,到这里已经可以满足你使用动态库的要求了,下面的就不用看了。 动态代理内部实现 首先来看看类Proxy的代码实现 Proxy的主要静态变量 // 映射表:用于维护类装载器对象到其对应的代理类缓存 private static Map loaderToCache = new WeakHashMap(); // 标记:用于标记一个动态代理类正在被创建中 private static Object pendingGenerationMarker = new Object(); // 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断 private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap()); // 我认为很关键的一个变量 protected InvocationHandler h; Proxy的构造方法 // 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用 private Proxy() {} // 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用 protected Proxy(InvocationHandler h) {this.h = h;} public static Object newProxyInstance(ClassLoader loader, Class[]interfaces,InvocationHandler h) throws IllegalArgumentException { // 检查 h 不为空,否则抛异常 if (h == null) { throw new NullPointerException(); } // 获得与指定类装载器和一组接口相关的代理类类型对象 Class cl = getProxyClass(loader, interfaces); // 通过反射获取构造函数对象并生成代理类实例 try { Constructor cons = cl.getConstructor(constructorParams); return (Object) cons.newInstance(new Object[] { h }); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { throw new InternalError(e.toString()); } } 从这里我们可以看出来,newProxyInstance 先通过getProxyClass生成了一个Class类型的对 象。然后通过构造类,生成一个自动生成的代理类的对象。 类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据: public static byte[] generateProxyClass(final String name, Class[] interfaces) 我们可以import sun.misc.ProxyGenerator,调用 generateProxyClass方法产生binary data,然后写入文件,最后通过反编译工具来查看内部实现原理。 反编译出来的代理类文件内容如下: import com.BankAccount; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class ProxyBankAccount extends Proxy implements BankAccount { private static Method m1; private static Method m3; private static Method m4; private static Method m0; private static Method m2; public ProxyBankAccount(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final double queryCount() throws { try { return ((Double)this.h.invoke(this, m3, null)).doubleValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void updateCount(double paramDouble) throws { try { this.h.invoke(this, m4, new Object[] { Double.valueOf(paramDouble) }); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.BankAccount").getMethod("queryCount", new Class[0]); m4 = Class.forName("com.BankAccount").getMethod("updateCount", new Class[] { Double.TYPE }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } } JDK动态代理绘图总结BankAccount interface String name; Double count; queryCount();updateCount(doublie i) BankAccount 对象A Proxy,静态方法 newProxyInstance ProxyFactory 类 ProxyFactory B 生成的代理类,继承proxy,实现bankAccount接口 Target属性 A对象句柄 ......... H 属性,继承自proxy,B对象的句柄 bing Invoke里面调用了method对象的invoke Invoke调用A queryCount()里面调用B对象的invoke 以上是Java JDK动态代理原理 Cglib动态代理 JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 Cglib是第三方的实现 参考网上:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html 示例  1、BookFacadeCglib.java  Java代码  1. package net.battier.dao;   2.    3. public interface BookFacade {   4.     public void addBook();   5. }     2、BookCadeImpl1.java  Java代码  1. package net.battier.dao.impl;   2.    3. /**  4.  * 这个是没有实现接口的实现类  5.  *   6.  * @author student  7.  *   8.  */   9. public class BookFacadeImpl1 {   10.     public void addBook() {   11.         System.out.println("增加图书的普通方法...");   12.     }   13. }   3、BookFacadeProxy.java  Java代码  1. package net.battier.proxy;   2.    3. import java.lang.reflect.Method;   4.    5. import net.sf.cglib.proxy.Enhancer;   6. import net.sf.cglib.proxy.MethodInterceptor;   7. import net.sf.cglib.proxy.MethodProxy;   8.    9. /**  10.  * 使用cglib动态代理  11.  *   12.  * @author student  13.  *   14.  */   15. public class BookFacadeCglib implements MethodInterceptor {   16.     private Object target;   17.    18.     /**  19.      * 创建代理对象  20.      *   21.      * @param target  22.      * @return  23.      */   24.     public Object getInstance(Object target) {   25.         this.target = target;   26.         Enhancer enhancer = new Enhancer();   27.         enhancer.setSuperclass(this.target.getClass());   28.         // 回调方法   29.         enhancer.setCallback(this);   30.         // 创建代理对象   31.         return enhancer.create();   32.     }   33.    34.     @Override   35.     // 回调方法   36.     public Object intercept(Object obj, Method method, Object[] args,   37.             MethodProxy proxy) throws Throwable {   38.         System.out.println("事物开始");   39.         proxy.invokeSuper(obj, args);   40.         System.out.println("事物结束");   41.         return null;   42.    43.    44.     }   45.    46. }   4、TestCglib.java  Java代码  1. package net.battier.test;   2.    3. import net.battier.dao.impl.BookFacadeImpl1;   4. import net.battier.proxy.BookFacadeCglib;   5.    6. public class TestCglib {   7.        8.     public static void main(String[] args) {   9.         BookFacadeCglib cglib=new BookFacadeCglib();   10.         BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());   11.         bookCglib.addBook();   12.     }   13. }   http://blog.csdn.net/xiaohai0504/article/details/6832990 不错的文章

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 5 金币 [ 分享文档获得金币 ]
1 人已下载

下载文档

相关文档