UDN-企业互联网技术人气社区

板块导航

浏览  : 992
回复  : 0

[讨论交流] 说说 Java 代理模式(1)

[复制链接]
cat77的头像 楼主
发表于 2016-9-14 10:32:35 | 显示全部楼层 |阅读模式
  代理实现可以分为静态代理和动态代理。

  静态代理

  静态代理模式其实很常见,比如买火车票这件小事:黄牛相当于是火车站的代理,我们可以通过黄牛买票,但只能去火车站进行改签和退票。在代码实现中相当于为一个委托对象realSubject提供一个代理对象proxy,通过proxy可以调用realSubject的部分功能,并添加一些额外的业务处理,同时可以屏蔽realSubject中未开放的接口。
1.jpg

  1、RealSubject 是委托类,Proxy 是代理类;

  2、Subject 是委托类和代理类的接口;

  3、request() 是委托类和代理类的共同方法;

  具体代码实现如下:
  1. interface Subject {
  2.     void request();
  3. }

  4. class RealSubject implements Subject {
  5.     public void request(){
  6.         System.out.println("RealSubject");
  7.     }
  8. }

  9. class Proxy implements Subject {
  10.     private Subject subject;

  11.     public Proxy(Subject subject){
  12.         this.subject = subject;
  13.     }
  14.     public void request(){
  15.         System.out.println("begin");
  16.         subject.request();
  17.         System.out.println("end");
  18.     }
  19. }

  20. public class ProxyTest {
  21.     public static void main(String args[]) {
  22.         RealSubject subject = new RealSubject();
  23.         Proxy p = new Proxy(subject);
  24.         p.request();
  25.     }
  26. }
复制代码

  静态代理实现中,一个委托类对应一个代理类,代理类在编译期间就已经确定。

  动态代理

  动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等,动态代理分为jdk动态代理和cglib动态代理,下面我们通过一个例子看看如何实现jdk动态代理。

  1、定义业务逻辑
  1. public interface Service {  
  2.     //目标方法
  3.     public abstract void add();  
  4. }

  5. public class UserServiceImpl implements Service {  
  6.     public void add() {  
  7.         System.out.println("This is add service");  
  8.     }  
  9. }
复制代码

  2、利用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口定义代理类的实现。
  1. class MyInvocatioHandler implements InvocationHandler {
  2.     private Object target;

  3.     public MyInvocatioHandler(Object target) {
  4.         this.target = target;
  5.     }

  6.     @Override
  7.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  8.         System.out.println("-----before-----");
  9.         Object result = method.invoke(target, args);
  10.         System.out.println("-----end-----");
  11.         return result;
  12.     }
  13.     // 生成代理对象
  14.     public Object getProxy() {
  15.         ClassLoader loader = Thread.currentThread().getContextClassLoader();
  16.         Class<?>[] interfaces = target.getClass().getInterfaces();
  17.         return Proxy.newProxyInstance(loader, interfaces, this);
  18.     }
  19. }
复制代码

  3、使用动态代理
  1. public class ProxyTest {
  2.     public static void main(String[] args) {
  3.         Service service = new UserServiceImpl();
  4.         MyInvocatioHandler handler = new MyInvocatioHandler(service);
  5.         Service serviceProxy = (Service)handler.getProxy();
  6.         serviceProxy.add();
  7.     }
  8. }
复制代码

  执行结果:
  1. -----before-----
  2. This is add service
  3. -----end-----
复制代码

  代理对象的生成过程由Proxy类的newProxyInstance方法实现,分为3个步骤:

  1、ProxyGenerator.generateProxyClass方法负责生成代理类的字节码,生成逻辑比较复杂,有兴趣的同学可以继续分析源码 sun.misc.ProxyGenerator;
  1. // proxyName:格式如 "com.sun.proxy.$Proxy.1";
  2. // interfaces:代理类需要实现的接口数组;
  3. // accessFlags:代理类的访问标识;
  4. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
复制代码

  2、native方法Proxy.defineClass0负责字节码加载的实现,并返回对应的Class对象。
  1. Class clazz = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
复制代码

  3、利用clazz.newInstance反射机制生成代理类的对象;

  反编译代理类

  为了更清楚的理解动态代理,通过以下方式把代理类字节码生成class文件。
  1. byte[] classFile = ProxyGenerator.generateProxyClass("com.sun.proxy.$Proxy.1", service.getClass().getInterfaces());
  2. FileOutputStream out = new FileOutputStream("com.sun.proxy.$Proxy.1.class");
  3. out.write(classFile);
  4. out.flush();
复制代码

  使用 反编译工具 jad jad com.sun.proxy.$Proxy.1 看看代理类如何实现,反编译出来的java代码如下:
  1. public final class $proxy1 extends Proxy implements Service {

  2.     public $proxy1(InvocationHandler invocationhandler) {
  3.         super(invocationhandler);
  4.     }

  5.     public final boolean equals(Object obj) {
  6.         try {
  7.             return ((Boolean)super.h.invoke(this, m1, new Object[] {
  8.                 obj
  9.             })).booleanValue();
  10.         }
  11.         catch(Error _ex) { }
  12.         catch(Throwable throwable) {
  13.             throw new UndeclaredThrowableException(throwable);
  14.         }
  15.     }

  16.     public final String toString() {
  17.         try {
  18.             return (String)super.h.invoke(this, m2, null);
  19.         }
  20.         catch(Error _ex) { }
  21.         catch(Throwable throwable) {
  22.             throw new UndeclaredThrowableException(throwable);
  23.         }
  24.     }

  25.     public final void add() {
  26.         try {
  27.             super.h.invoke(this, m3, null);
  28.             return;
  29.         }
  30.         catch(Error _ex) { }
  31.         catch(Throwable throwable) {
  32.             throw new UndeclaredThrowableException(throwable);
  33.         }
  34.     }

  35.     public final int hashCode() {
  36.         try {
  37.             return ((Integer)super.h.invoke(this, m0, null)).intValue();
  38.         }
  39.         catch(Error _ex) { }
  40.         catch(Throwable throwable) {
  41.             throw new UndeclaredThrowableException(throwable);
  42.         }
  43.     }

  44.     private static Method m1;
  45.     private static Method m2;
  46.     private static Method m3;
  47.     private static Method m0;

  48.     static {
  49.         try {
  50.             m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
  51.                 Class.forName("java.lang.Object")
  52.             });
  53.             m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  54.             m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]);
  55.             m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  56.         }
  57.         catch(NoSuchMethodException nosuchmethodexception) {
  58.             throw new NoSuchMethodError(nosuchmethodexception.getMessage());
  59.         }
  60.         catch(ClassNotFoundException classnotfoundexception) {
  61.             throw new NoClassDefFoundError(classnotfoundexception.getMessage());
  62.         }
  63.     }
  64. }
复制代码

  从上述代码可以发现:

  1、生成的$proxy1继承自Proxy类,并实现了Service接口。

  2、执行代理对象的方法,其实就是执行InvocationHandle对象的invoke方法,传入的参数分别是当前代理对象,当前执行的方法和参数。
  1. super.h.invoke(this, m3, null);
复制代码

相关帖子

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部