美文网首页
Java设计模式之10_Proxy(代理模式)

Java设计模式之10_Proxy(代理模式)

作者: d26168ad953a | 来源:发表于2017-05-15 12:15 被阅读654次

更多Java设计模式:Java设计模式-目录

一、代理简介

为某个对象提供一个代理,以控制对这个对象的访问。
  代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。


代理模式

从图中可以看出,代理接口(Subject)、代理类(ProxySubject)、委托类(RealSubject)形成一个“品”字结构。根据代理类的生成时间不同可以将代理分为静态代理和动态代理两种。
  下面以一个模拟需求说明静态代理和动态代理:委托类要处理一项耗时较长的任务,客户类需要打印出执行任务消耗的时间。解决这个问题需要记录任务执行前时间和任务执行后时间,两个时间差就是任务执行消耗的时间。

二、静态代理

由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

2.1、代码分析

代理接口

public interface Subject { 
  public void doSubject();
}

委托类,具体处理业务。

  public class RealSubject implements Subject { 
  @Override 
  public void doSubject() { 
    try { 
      System.out.println("doing subject..."); 
      Thread.sleep(new Random().nextInt(1000)); 
    } catch (InterruptedException e) { 
      e.printStackTrace(); 
    } 
  }
}

静态代理类

public class TimeProxy implements Subject { 
  private Subject realSubject; 
  public TimeProxy(RealSubject r) { 
    super(); this.realSubject = r; 
  } 
  @Override 
  public void doSubject() { 
    System.out.println("start..."); 
    long start = System.currentTimeMillis(); 
    realSubject.doSubject(); 
    System.out.println("end...costing " + (System.currentTimeMillis() - start) + " ms"); 
  }
}
public class LogProxy implements Subject { 
  public LogProxy(Subject r) { 
    super(); 
    this.r = r; 
  }
  private Subject r; 
  @Override 
  public void doSubject() { 
    System.out.println("log start..."); 
    r.doSubject(); System.out.println("log end..."); 
  }
}

客户类

public class Client { 
  public static void main(String[] args) { 
    RealSubject realSubject = new RealSubject(); 
    TimeProxy timeProxy = new TimeProxy(realSubject); 
    LogProxy logProxy = new LogProxy(timeProxy);
    logProxy.doSubject(); 
  }
}

输出

log start...start...doing subject...end...costing 189 mslog end...

2.2、静态代理类优缺点

优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
  缺点:
   1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
   2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

三、动态代理

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定。

3.1、先看看与动态代理紧密关联的Java API。

1)java.lang.reflect.Proxy
  这是 Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。

Proxy类的静态方法

// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
static InvocationHandler getInvocationHandler(Object proxy)
// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
// 方法 3:该方法用于判断指定类对象是否是一个动态代理类
static boolean isProxyClass(Class cl)
// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

2)java.lang.reflect.InvocationHandler
  这是调用处理器接口,它自定义了一个 invoke方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。
  InvocationHandler的核心方法

// 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象
// 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
Object invoke(Object proxy, Method method, Object[] args)

3)java.lang.ClassLoader
  这是类装载器类,负责将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。
每次生成动态代理类对象时都需要指定一个类装载器对象

3.2、动态代理实现步骤

具体步骤是:
  a. 实现InvocationHandler接口创建自己的调用处理器
  b. 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
  c. 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
  d. 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象

分步骤实现动态代理

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发

// 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用 InvocationHandler handler = new InvocationHandlerImpl(..);
// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

Proxy类的静态方法newProxyInstance对上面具体步骤的后三步做了封装,简化了动态代理对象的获取过程。

简化后的动态代理实现

// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
InvocationHandler handler = new InvocationHandlerImpl(..);
// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );

3.3、动态代理实现示例

3.3.1、JDK动态

定义一个接口

public interface HelloWorld { 
  public void sayHello();
}

类HelloWorldImpl是HelloWorld接口的实现

public class HelloWorldImpl implements HelloWorld { 
  @Override 
  public void sayHello() { 
    System.out.println("hello world"); 
  }
}

HelloWorldHandler是 InvocationHandler接口实现

public class HelloWorldHandler implements InvocationHandler { 
  private Object object; 
  public HelloWorldHandler(Object obj) { 
    super(); this.object = obj; 
  } 
  @Override 
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
    Object result = null; doBefore(); 
    result = method.invoke(object, args); 
    doAfter(); 
    return result; 
  } 
  private void doAfter() { 
    System.out.println("after method invoke..."); 
  } 
  private void doBefore() { 
    System.out.println("before method invoke..."); 
  }
}

测试类:

public class HelloWorldTest { 
  public static void main(String[] args) { 
    HelloWorld helloWorld = new HelloWorldImpl(); 
    InvocationHandler invocationHandler = new HelloWorldHandler(helloWorld); 
    HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(helloWorld.getClass().getClassLoader(), 
    helloWorld.getClass().getInterfaces(), invocationHandler); 
    proxy.sayHello(); 
  }
}

运行结果为:

before method invoke...hello worldafter method invoke...

3.3.2 CGlib动态代理
  需要引入cglib的jar和对应的asm的jar,注意需要对应上,不然会报错。
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

首先定义接口:

public interface BookFacade { 
  public void addBook();
}

定义实现类:

public class BookFacadeImpl implements BookFacade { 
  @Override 
  public void addBook() { 
    System.out.println("新增图书..."); 
  }
}

cglib代理类:

public class BookFacadeCglib implements MethodInterceptor { 
  private Object target; 
  /** 
  * 创建代理对象 
  * 
  * @param target 
  * @return 
  */ 
  public Object getInstance(Object target) { 
    this.target = target; 
    Enhancer enhancer = new Enhancer(); 
    enhancer.setSuperclass(this.target.getClass()); 
    // 回调方法 
    enhancer.setCallback(this); 
    // 创建代理对象
     return enhancer.create(); 
  } 
  @Override 
  public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 
    System.out.println("事物开始"); 
    methodProxy.invokeSuper(obj, args); 
    System.out.println("事物结束"); return null; 
  }
}

测试类:

public class TestCglib { 
public static void main(String[] args) { 
BookFacadeCglib cglib = new BookFacadeCglib(); 
BookFacadeImpl bookCglib = (BookFacadeImpl) cglib.getInstance(new BookFacadeImpl()); 
bookCglib.addBook();
}
}

返回结果:

事物开始新增图书...事物结束

纯纯代码兴味索然
感谢阅读收获归你
不妥之处敬请指教

加我微信

相关文章

  • Java设计模式之10_Proxy(代理模式)

    更多Java设计模式:Java设计模式-目录 一、代理简介 为某个对象提供一个代理,以控制对这个对象的访问。代理类...

  • Java代理模式之JDK动态代理

    了解什么是动态代理模式,可参考Java设计模式之代理模式 简介 JDK动态代理是java.lang.reflect...

  • java之代理技术

    java之代理模式 直接聊技术! 描述 代理模式是常用的java设计模式,代理类主要负责为委托类预处理消息、过滤消...

  • Java设计模式之代理模式

    Java设计模式之代理模式 代理模式 静态代理 动态代理 为什么需要代理 通过代理,我们能够不用知道委托人是谁,而...

  • java动态代理(JDK和cglib)(转载自http://ww

    java动态代理(JDK和cglib) JAVA的动态代理 代理模式 代理模式是常用的java设计模式,他的特征是...

  • Spring AOP源码解析

    欲了解AOP,需先了解Java动态代理;欲了解Java动态代理,先熟悉设计模式之代理模式。入门有道,先从简学。 1...

  • java设计模式之代理模式(静态代理)

      今天给大家分享的是java设计模式之代理模式中的静态代理模式,动态代理模式将在后面文章中给出。如有不足,敬请指...

  • Java代理模式之CGLIB动态代理

    了解什么是动态代理模式,可参考Java设计模式之代理模式 简介 前面我们了解了JDK动态代理技术,发现其真实对象必...

  • java建造者模式

    其他设计模式java单例模式java建造者模式java策略模式java代理模式java观察者模式java适配器模式...

  • java单例模式

    其他设计模式java单例模式java建造者模式java策略模式java代理模式java观察者模式java适配器模式...

网友评论

      本文标题:Java设计模式之10_Proxy(代理模式)

      本文链接:https://www.haomeiwen.com/subject/mjxgtxtx.html