• 读书笔记_java设计模式深入研究 第七章 代理模式 Proxy


    1,代理模式:一个类可以用作其他东西的接口,其他类可以通过此类去访问被代理者。
    2,UML图如下:


    3,角色:
        ISubject:抽象主题接口,该接口定义对象和它的代理共用的接口。
        RealSubject:真实主题角色,使实现抽象主题接口的类。
        Proxy:代理角色,内部含有对真实对象的引用,从而可以操作真实对象,代理对象提供与真实对象相同的接口,以便在任何时候都可以替代真实对象。
    4,简单代码:

    package pattern.chp07.proxy;
     
    /**
    * 类描述:抽象主题接口
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 24, 2014 3:51:12 PM Jing Created.
    *
    */
    public interface ITV {
    /**
    *
    * 方法说明:购买电视
    *
    * Author: Jing
    * Create Date: Dec 24, 2014 3:51:35 PM
    *
    * @return void
    */
    public void buyTV();
    }

    package pattern.chp07.proxy;
     
    /**
    * 类描述:真实主题角色
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 24, 2014 3:53:25 PM Jing Created.
    *
    */
    public class Buyer implements ITV {
     
    public void buyTV() {
    System.out.println("TV");
    }
     
    }
    package pattern.chp07.proxy;
     
    /**
    * 类描述:代理对象
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 24, 2014 3:54:04 PM Jing Created.
    *
    */
    public class BuyerProxy implements ITV{
    private Buyer buyer;
    public void buyTV() {
    System.out.println("Proxy buy ...");
    buyer.buyTV();
    }
     
    }
    5,远程代理:为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可在另一台主机中。
    6,计数代理:简单例子,实现图书计数功能。

     
    package pattern.chp07.proxy.book;
     
    /**
    * 类描述:抽象主题
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 24, 2014 4:23:56 PM Jing Created.
    *
    */
    public interface IBorrow {
    /**
    *
    * 方法说明:借阅
    *
    * Author: Jing
    * Create Date: Dec 24, 2014 4:24:18 PM
    *
    * @return
    * @return boolean
    */
    boolean borrow();
    }
    /**
    * 类描述:借阅实现类
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 25, 2014 9:41:09 AM Jing Created.
    *
    */
    public class Borrow implements IBorrow {
     
    private Book book;
     
    public Book getBook() {
    return book;
    }
     
    public void setBook(Book book) {
    this.book = book;
    }
     
    public boolean borrow() {
    //保存数据
    //some code
    return true;
    }
     
    }
     
    package pattern.chp07.proxy.book;
     
    import java.io.RandomAccessFile;
    import java.util.Calendar;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Set;
     
    /**
    * 类描述:借书代理
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 25, 2014 9:43:32 AM Jing Created.
    *
    */
    public class BorrowProxy implements IBorrow {
     
    private Borrow obj;
     
    private Map<String, Integer> map;
     
    public BorrowProxy(Borrow obj) {
     
    this.obj = obj;
    map = new HashMap<String, Integer>();
    }
     
    public boolean borrow() {
     
    if (!obj.borrow()) {
     
    return false;
    }
     
    Book book = obj.getBook();
    Integer i = map.get(book.getNo());
    i = (i == null) ? 1 : i + 1;
    map.put(book.getNo(), i);
    return true;
    }
     
    /**
    *
    * 方法说明:记录日志
    *
    * Author: Jing Create Date: Dec 25, 2014 10:07:37 AM
    *
    * @throws Exception
    * @return void
    */
    public void log() throws Exception {
     
    Set<String> set = map.keySet();
    String key = "";
    String result = "";
     
    Iterator<String> it = set.iterator();
    while (it.hasNext()) {
     
    key = it.next();
    result += key + " " + map.get(key) + " ";
    }
    Calendar c = Calendar.getInstance();
    RandomAccessFile fa = new RandomAccessFile("c:/log.txt", "rw");
    fa.seek(fa.length());
    fa.writeBytes(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-"
    + c.get(Calendar.DAY_OF_MONTH) + " ");
    fa.writeBytes(result);
    fa.close();
    }
     
    }
     
    package pattern.chp07.proxy.book;
     
    /**
    * 类描述:书籍实体类
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 24, 2014 4:21:57 PM Jing Created.
    *
    */
    public class Book {
    /**
    * 书号
    */
    private String no;
    /**
    * 书名
    */
    private String name;
     
    public Book(String no, String name) {
    super();
    this.no = no;
    this.name = name;
    }
     
    package pattern.chp07.proxy.book;
     
    /**
    * 类描述:
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 25, 2014 10:20:58 AM Jing Created.
    *
    */
    public class MainTest {
     
    public static void main(String[] args) throws Exception {
    Borrow br = new Borrow();
    BorrowProxy proxy = new BorrowProxy(br);
    Book book = new Book("1000", "cesf");
    br.setBook(book);
    proxy.borrow();//借阅
    book = new Book("1001", "sdadasd");
    br.setBook(book);
    proxy.borrow();
    proxy.log();
    }
    }
    ​7,JDK动态代理
      使用jdk动态代理实现对接收信件的计数。
    package pattern.chp07.proxy.emailCount;
     
    /**
    * 类描述:抽象主题
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 25, 2014 11:01:05 AM Jing Created.
    *
    */
    public interface IRegister {
    /**
    *
    * 方法说明:等级来信
    *
    * Author: Jing
    * Create Date: Dec 25, 2014 11:01:44 AM
    *
    * @param msg
    * @return void
    */
    void regist(String msg);
    }

    package pattern.chp07.proxy.emailCount;
     
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
     
    /**
    * 类描述:创建代理类
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 25, 2014 11:09:45 AM Jing Created.
    *
    */
    public class GenericProxy {
     
    public static Object createProxy(Object obj, InvocationHandler invokeObj) {
     
    Object proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass()
    .getInterfaces(), invokeObj);
    return proxy;
    }
    }
    /**
    * 类描述:Email信件
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 25, 2014 11:05:15 AM Jing Created.
    *
    */
    public class EmailFrom implements IRegister {
     
    public void regist(String msg) {
    System.out.println("From Email");
    }
     
    }
    package pattern.chp07.proxy.emailCount;
     
    /**
    * 类描述:来自传统邮件
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 25, 2014 11:06:03 AM Jing Created.
    *
    */
    public class PostFrom implements IRegister{
     
    public void regist(String msg) {
    System.out.println("From Post!");
    }
     
    }
     
    /**
    * 类描述:计数器动态代理
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 25, 2014 11:07:22 AM Jing Created.
    *
    */
    public class CountInvoke implements InvocationHandler {
     
    private int count = 0;
    /**
    * 主题对象
    */
    private Object obj;
     
    public CountInvoke(Object obj) {
    this.obj = obj;
    }
     
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     
    count++;
    method.invoke(obj, args);
    return null;
    }
     
    public int getCount() {
    return count;
    }
     
    public void setCount(int count) {
    this.count = count;
    }
     
    }
    public class Test {
     
    public static void main(String[] args) {
    IRegister email = new EmailFrom();
    IRegister post = new PostFrom();
    CountInvoke emailInvoke = new CountInvoke(email);
    CountInvoke postInvoke = new CountInvoke(post);
    IRegister emailproxy = (IRegister) GenericProxy.createProxy(email, emailInvoke);
    IRegister postproxy = (IRegister) GenericProxy.createProxy(email, postInvoke);
    emailproxy.regist("email");
    postproxy.regist("post");
    System.out.println(emailInvoke.getCount());
    postproxy.regist("post2");
    System.out.println(postInvoke.getCount());
    }
    }
    8,JDK源码:
        -1,Proxy:
        Proxy最主要的方法是Proxy.newProxyInstance方法,我们可以查看对应JDK  API文档:
        动态代理类是一个实现在创建类时,指定该类运行时的接口列表的类,该类具有如下行为。代理接口是代理类实现的一个接口。代理实例是代理类的一个实例。每个代理实例都有一个关联的调用处理程序对象,它可以实现InvocationHandler接口。通过其中一个代理接口的代理实例上的方法将被指派到实例的调用处理程序的invoke方法,并传递代理实例,识别调用方法的Method对象以及包含的参数。调用处理程序以适当的方法处理参数编码,并调用方法,将方法执行结果返回。
        代理类具有以下属性:
            代理类是公共的、最终的而不是抽象的。
            未指定代理类的非限定名称,但是以字符串"$Proxy"开头的类名空间应该为代理类保留。
            代理类扩展Proxy。
            代理类会按一定顺序准确的实现其创建时的接口。
            如果代理类实现了非公共接口,那么它将在与该接口相同的包中定义。
            由于代理类实现了其在创建时指定的接口,所以对其Class对象调用getInterfaces将返回一个包好相同接口列表的数组,对齐Class对象调用getMethods方法将返回一个包含这些接口所有方法的Method对象数组,并且调用getMethod将会在代理接口中找到期望的一些方法。
            如果Proxy.isProxyClass方法传递代理类,则该方法返回true。
            每个代理类都有一个可以带一个参数(接口InvocationHandler)的公共构造方法,用于设置代理程序的调用处理程序。并非使用反射才能访问公共构造方法,通过Proxy.newInstance方法也可以返回代理实例。
            
    newProxyInstance
     
    public static Object newProxyInstance(ClassLoader loader,
    Class<?>[] interfaces,
    InvocationHandler h)
    throws IllegalArgumentException
    返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。此方法相当于:
    Proxy.getProxyClass(loader, interfaces).
    getConstructor(new Class[] { InvocationHandler.class }).
    newInstance(new Object[] { handler });
    Proxy.newProxyInstance 抛出 IllegalArgumentException,原因与 Proxy.getProxyClass 相同。
     
    参数:
    loader - 定义代理类的类加载器
    interfaces - 代理类要实现的接口列表
    h - 指派方法调用的调用处理程序
    返回:
    一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
    抛出:
    IllegalArgumentException - 如果违反传递到 getProxyClass 的参数上的任何限制
    NullPointerException - 如果 interfaces 数组参数或其任何元素为 null,或如果调用处理程序 h null
    -2,接口InvocationHandler
        该接口是代理实例的调用处理程序实现的接口。
        每个代理实例都有一个关联的调用处理程序,对代理实例调用方法的时,将方法进行编码并指派到它的调用处理程序的invoke方法。
    invoke
     
    Object invoke(Object proxy,
    Method method,
    Object[] args)
    throws Throwable
    在代理实例上处理方法调用并返回结果。在与方法关联的代理实例上调用方法时,将在调用处理程序上调用此方法。
    参数:
    proxy - 在其上调用方法的代理实例
    method - 对应于在代理实例上调用的接口方法的 Method 实例。 Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
    args - 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 java.lang.Integer java.lang.Boolean)的实例中。
    返回:
    从代理实例的方法调用返回的值。如果接口方法的声明返回类型是基本类型,则此方法返回的值一定是相应基本包装对象类的实例;否则,它一定是可分配到声明返回类型的类型。如果此方法返回的值为 null 并且接口方法的返回类型是基本类型,则代理实例上的方法调用将抛出 NullPointerException。否则,如果此方法返回的值与上述接口方法的声明返回类型不兼容,则代理实例上的方法调用将抛出 ClassCastException
    抛出:
    Throwable - 从代理实例上的方法调用抛出的异常。该异常的类型必须可以分配到在接口方法的 throws 子句中声明的任一异常类型或未经检查的异常类型 java.lang.RuntimeException java.lang.Error。如果此方法抛出经过检查的异常,该异常不可分配到在接口方法的 throws 子句中声明的任一异常类型,代理实例的方法调用将抛出包含此方法曾抛出的异常的 UndeclaredThrowableException
    -3,使用代码:
    /**
    * 类描述:动态代理实例
    *
    * @author: Jing
    * @version $Id: Exp$
    *
    * History: Dec 25, 2014 3:25:08 PM Jing Created.
    *
    */
    public class ProxyStudy {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws Exception {
     
    Class<?> clazz = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
    System.out.println("动态代理产生的类名为: " + clazz.getName());
     
    // 获取动态代理产生的类的构造方法
    Constructor<?>[] cons = clazz.getConstructors();
    for (Constructor<?> con : cons) {
     
    System.out.println("构造方法: " + con.getName() + "----对应参数: "
    + Arrays.asList(con.getParameterTypes()));
    }
     
    // 获取动态类的普通方法
    Method[] methods = clazz.getDeclaredMethods();
    for (Method method : methods) {
     
    System.out.println("方法:" + method.getName() + "----对应参数: "
    + Arrays.asList(method.getParameterTypes()));
    }
     
    // 构造方法需要InvocationHandler对象
    Constructor<?> constructor = clazz.getConstructor(InvocationHandler.class);
    Collection<Object> proxyBuildCollection = (Collection<Object>) constructor
    .newInstance(new InvocationHandler() {
    ArrayList<Object> target = new ArrayList<Object>();
     
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable {
    System.out.println("执行方法 : " + method.getName() + "前,参数为:"
    + args.toString());
    Object result = method.invoke(target, args);
    System.out.println("执行方法 : " + method.getName() + "后。");
    return result;
    }
    });
    proxyBuildCollection.add("asd");
    proxyBuildCollection.add("asd");
    proxyBuildCollection.add("asd");
    }
    }
    9,JDK动态代理原理:

    欢迎转载,但转载请注明原文链接[博客园: http://www.cnblogs.com/jingLongJun/]
    [CSDN博客:http://blog.csdn.net/mergades]。
    如相关博文涉及到版权问题,请联系本人。
  • 相关阅读:
    dotnetcore EF 分页
    MySQL为什么有时候会选错索引?
    Java内存模型:看Java如何解决可见性和有序性问题
    互斥锁(上):解决原子性问题
    可见性、原子性和有序性问题:并发编程Bug的源头
    程序的运行过程:从代码到机器运行
    几行汇编几行C:实现一个最简单的内核
    怎么给字符串字段加索引?
    互斥锁(下):如何用一把锁保护多个资源?
    震撼的Linux全景图:业界成熟的内核架构长什么样?
  • 原文地址:https://www.cnblogs.com/jingLongJun/p/4491069.html
Copyright © 2020-2023  润新知