请选择 进入手机版 | 继续访问电脑版

技术控

    今日:33| 主题:54646
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] Java动态代理与CGLIB

[复制链接]
Raging 发表于 2016-10-19 07:55:13
309 4

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
1. 静态代理模式

  因为需要对一些函数进行二次处理,或是某些函数不让外界知道时,可以使用代理模式,通过访问第三方,间接访问原函数的方式,达到以上目的,来看一下代理模式的类图:
   

Java动态代理与CGLIB

Java动态代理与CGLIB-1-技术控-interface,Welcome,public,return,第三方

  1. interface Hosee{
  2.         String sayhi();
  3. }

  4. class Hoseeimpl implements Hosee{

  5.         @Override
  6.         public String sayhi()
  7.         {
  8.                 return "Welcome oschina hosee's blog";
  9.         }

  10. }

  11. class HoseeProxy implements Hosee{

  12.         Hosee h;

  13.         public HoseeProxy(Hosee h)
  14.         {
  15.                 this.h = h;
  16.         }

  17.         @Override
  18.         public String sayhi()
  19.         {
  20.                 System.out.println("I'm proxy!");
  21.                 return h.sayhi();
  22.         }

  23. }

  24. public class StaticProxy
  25. {

  26.         public static void main(String[] args)
  27.         {
  28.                 Hoseeimpl h = new Hoseeimpl();
  29.                 HoseeProxy hp = new HoseeProxy(h);
  30.                 System.out.println(hp.sayhi());
  31.         }

  32. }
复制代码
1.1 静态代理的弊端

  如果要想为多个类进行代理,则需要建立多个代理类,维护难度加大。
  仔细想想,为什么静态代理会有这些问题,是因为代理在编译期就已经决定,如果代理哪个发生在运行期,这些问题解决起来就比较简单,所以动态代理的存在就很有必要了。
  2. 动态代理

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Proxy;

  4. interface HoseeDynamic
  5. {
  6.         String sayhi();
  7. }

  8. class HoseeDynamicimpl implements HoseeDynamic
  9. {
  10.         @Override
  11.         public String sayhi()
  12.         {
  13.                 return "Welcome oschina hosee's blog";
  14.         }
  15. }

  16. class MyProxy implements InvocationHandler
  17. {
  18.         Object obj;
  19.         public Object bind(Object obj)
  20.         {
  21.                 this.obj = obj;
  22.                 return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
  23.                                 .getClass().getInterfaces(), this);
  24.         }
  25.         @Override
  26.         public Object invoke(Object proxy, Method method, Object[] args)
  27.                         throws Throwable
  28.         {
  29.                 System.out.println("I'm proxy!");
  30.                 Object res = method.invoke(obj, args);
  31.                 return res;
  32.         }
  33. }

  34. public class DynamicProxy
  35. {
  36.         public static void main(String[] args)
  37.         {
  38.                 MyProxy myproxy = new MyProxy();
  39.                 HoseeDynamicimpl dynamicimpl = new HoseeDynamicimpl();
  40.                 HoseeDynamic proxy = (HoseeDynamic)myproxy.bind(dynamicimpl);
  41.                 System.out.println(proxy.sayhi());
  42.         }
  43. }
复制代码
类比静态代理,可以发现,代理类不需要实现原接口了,而是实现InvocationHandler。通过
  1. Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
  2.                                 .getClass().getInterfaces(), this);
复制代码
来动态生成一个代理类,该类的类加载器与被代理类相同,实现的接口与被代理类相同。
  通过上述方法生成的代理类相当于静态代理中的代理类。
  这样就实现了在运行期才决定代理对象是怎么样的,解决了静态代理的弊端。
  当动态生成的代理类调用方法时,会触发invoke方法,在invoke方法中可以对被代理类的方法进行增强。
  通过动态代理可以很明显的看到它的好处,在使用静态代理时,如果不同接口的某些类想使用代理模式来实现相同的功能,将要实现多个代理类,但在动态代理中,只需要一个代理类就好了。
  除了省去了编写代理类的工作量,动态代理实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景中。
  2.1 动态代理的弊端

  代理类和委托类需要都实现同一个接口。也就是说只有实现了某个接口的类可以使用Java动态代理机制。但是,事实上使用中并不是遇到的所有类都会给你实现一个接口。因此,对于没有实现接口的类,就不能使用该机制。
  而CGLIB则可以实现对类的动态代理
  2.2 回调函数原理

  上文说了,当动态生成的代理类调用方法时,会触发invoke方法。
  很显然invoke方法并不是显示调用的,它是一个回调函数,那么回调函数是怎么被调用的呢?
  上述动态代理的代码中,唯一不清晰的地方只有
  1. Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
  2.                                 .getClass().getInterfaces(), this);
复制代码
跟踪这个方法的源码,可以看到程序进行了验证、优化、缓存、同步、生成字节码、显示类加载等操作,前面的步骤并不是我们关注的重点,而最后它调用了
  1. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  2.                 proxyName, interfaces);
复制代码
该方法用来完成生成字节码的动作,这个方法可以在运行时产生一个描述代理类的字节码byte[]数组。
  在main函数中加入
  1. System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
复制代码
加入这句代码后再次运行程序,磁盘中将会产生一个名为”$Proxy().class”的代理类Class文件,反编译(反编译工具我使用的是 JD-GUI )后可以看见如下代码:
  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Proxy;
  4. import java.lang.reflect.UndeclaredThrowableException;

  5. public final class $Proxy0 extends Proxy
  6.   implements HoseeDynamic
  7. {
  8.   private static Method m1;
  9.   private static Method m3;
  10.   private static Method m0;
  11.   private static Method m2;

  12.   public $Proxy0(InvocationHandler paramInvocationHandler)
  13.     throws
  14.   {
  15.     super(paramInvocationHandler);
  16.   }

  17.   public final boolean equals(Object paramObject)
  18.     throws
  19.   {
  20.     try
  21.     {
  22.       return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  23.     }
  24.     catch (Error|RuntimeException localError)
  25.     {
  26.       throw localError;
  27.     }
  28.     catch (Throwable localThrowable)
  29.     {
  30.       throw new UndeclaredThrowableException(localThrowable);
  31.     }
  32.   }

  33.   public final String sayhi()
  34.     throws
  35.   {
  36.     try
  37.     {
  38.       return (String)this.h.invoke(this, m3, null);
  39.     }
  40.     catch (Error|RuntimeException localError)
  41.     {
  42.       throw localError;
  43.     }
  44.     catch (Throwable localThrowable)
  45.     {
  46.       throw new UndeclaredThrowableException(localThrowable);
  47.     }
  48.   }

  49.   public final int hashCode()
  50.     throws
  51.   {
  52.     try
  53.     {
  54.       return ((Integer)this.h.invoke(this, m0, null)).intValue();
  55.     }
  56.     catch (Error|RuntimeException localError)
  57.     {
  58.       throw localError;
  59.     }
  60.     catch (Throwable localThrowable)
  61.     {
  62.       throw new UndeclaredThrowableException(localThrowable);
  63.     }
  64.   }

  65.   public final String toString()
  66.     throws
  67.   {
  68.     try
  69.     {
  70.       return (String)this.h.invoke(this, m2, null);
  71.     }
  72.     catch (Error|RuntimeException localError)
  73.     {
  74.       throw localError;
  75.     }
  76.     catch (Throwable localThrowable)
  77.     {
  78.       throw new UndeclaredThrowableException(localThrowable);
  79.     }
  80.   }

  81.   static
  82.   {
  83.     try
  84.     {
  85.       m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  86.       m3 = Class.forName("HoseeDynamic").getMethod("sayhi", new Class[0]);
  87.       m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  88.       m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  89.       return;
  90.     }
  91.     catch (NoSuchMethodException localNoSuchMethodException)
  92.     {
  93.       throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  94.     }
  95.     catch (ClassNotFoundException localClassNotFoundException)
  96.     {
  97.       throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  98.     }
  99.   }
  100. }
复制代码
动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。
  可以在上述代码中看到,无论调用哪个方法,都会调用到InvocationHandler的invoke方法,只是参数不同。
  2.3 动态代理与静态代理的区别

  
       
  • Proxy类的代码被固定下来,不会因为业务的逐渐庞大而庞大;   
  • 可以实现AOP编程,这是静态代理无法实现的;   
  • 解耦,如果用在web业务下,可以实现数据层和业务层的分离。   
  • 动态代理的优势就是实现无侵入式的代码扩展。 静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题  
  3. CGLIB

  cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
  1. import java.lang.reflect.Method;

  2. import net.sf.cglib.proxy.Enhancer;
  3. import net.sf.cglib.proxy.MethodInterceptor;
  4. import net.sf.cglib.proxy.MethodProxy;

  5. class CGlibHosee
  6. {
  7.         public String sayhi()
  8.         {
  9.                 return "Welcome oschina hosee's blog";
  10.         }
  11. }

  12. class CGlibHoseeProxy
  13. {
  14.         Object obj;

  15.         public Object bind(final Object target)
  16.         {
  17.                 this.obj = target;
  18.                 Enhancer enhancer = new Enhancer();
  19.                 enhancer.setSuperclass(obj.getClass());
  20.                 enhancer.setCallback(new MethodInterceptor()
  21.                 {
  22.                         @Override
  23.                         public Object intercept(Object obj, Method method, Object[] args,
  24.                                         MethodProxy proxy) throws Throwable
  25.                         {
  26.                                 System.out.println("I'm proxy!");
  27.                                 Object res = method.invoke(target, args);
  28.                                 return res;
  29.                         }
  30.                 });
  31.                 return enhancer.create();
  32.         }

  33. }

  34. public class CGlibProxy
  35. {
  36.         public static void main(String[] args)
  37.         {
  38.                 CGlibHosee cGlibHosee = new CGlibHosee();
  39.                 CGlibHoseeProxy cGlibHoseeProxy = new CGlibHoseeProxy();
  40.                 CGlibHosee proxy = (CGlibHosee) cGlibHoseeProxy.bind(cGlibHosee);
  41.                 System.out.println(proxy.sayhi());
  42.         }
  43. }
复制代码
cglib需要指定父类和回调方法。当然cglib也可以与Java动态代理一样面向接口,因为本质是继承。
  Reference:

  1. http://blog.csdn.net/lidatgb/article/details/8941711
  2. http://shensy.iteye.com/blog/1698197
  3. http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html
  4. http://www.shangxueba.com/jingyan/1853835.html
  5. 《深入理解Java虚拟机》
  6. http://paddy-w.iteye.com/blog/841798



上一篇:小白用户教你免费搭建微商城|搜狐快站的经验
下一篇:从建设到治理,从系统到团队,谈高可用架构之道
黄胜帅 发表于 2016-10-19 23:36:14
Raging敢整点更有创意的不?兄弟们等着围观捏~
回复 支持 反对

使用道具 举报

正能量 发表于 2016-10-20 04:24:08
生前何必久睡,死后自会长眠……
回复 支持 反对

使用道具 举报

陈涛 发表于 2016-10-26 11:06:15
此贴真乃高大上
回复 支持 反对

使用道具 举报

难拥友 发表于 2016-10-27 06:53:36
撸过。。。。
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读


回页顶回复上一篇下一篇回列表
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )

© 2001-2017 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表