zoukankan      html  css  js  c++  java
  • Proxy模式(代理[延迟]模式|Java最全)

    Proxy??

    Proxy是“代理人”的意思,它指的是代替别人进行工作的人。代理实际上就是使用委托的机制,在代理的过程中你可以做点其他的事情,然后再来执行被代理对象的代码。

    • 知识储备

    1.什么时候使用:

    GoF书(请参见附录E[GoF])在讲解Proxy模式时,使用了一个可以在文本中嵌入图形对象(例如图片等)的文本编辑器作为例子。为了生成这些图形对象,需要读取图片文件,这很耗费时间。因此如果在打开文档时就生成有所的图形对象,就会导致文档打开时间过长。所以,最好是当用户浏览至文本中各个图形对象时,再去生成它们的实例。这时,Proxy模式就有了用武之地。

    2.有那些代理:

    • Virtual Proxy(虚拟代理)Virtual Proxy就是本章中学习的Proxy模式。只有当真正需要实例时,它才生成和初始化实例。

    • Remote Proxy(远程代理)Remote Proxy可以让我们完全不必在意RealSubject角色是否在远程网络上,可以如同它在自己身边一样(透明性地)调用它的 方法。Java的RMI(RemoteMethodInvocation:远程方法调用)就相当于Remote Proxy。

    • Access Proxy Access Proxy 用于在调用RealSubject角色的功能时设置访问限制。例如,这种代理可以只允许指定的用户调用方法,而当其他用户调用方法时则报错。

    • 静态代理
      1.使用委托机制代理人只代理他能解决的问题。当遇到他不能解决的问题时,还是会“转交”给本人去解决。
      这里的“转交”就是在本书中多次提到过的“委托”。从PrinterProxy类的print方法中调用real.print方法正是这种“委托”的体现。

    1. 为了实现PrinterProxy类可以从printer类中分离出来作为独立的组件使用,而且只要是实现了Printable接口的类都可以扮演Proxy的角色。需要使用反射实例
    
    private synchronized void realize(String classname) {
        if(real==null){
            try {
                real = ((Printable) Class.forName(classname).newInstance());
            }catch (ClassNotFoundException e){
                System.out.println("没有发现"+classname);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    
    
    
    • 动态代理

    在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:
    InvocationHandler:

    
    InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 
    
    Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance,
    the method invocation is encoded and dispatched to the invoke method of its invocation handler.
    
    
    

    每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看
    InvocationHandler这个接口的唯一一个方法 invoke 方法:
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable

    我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?

    
    Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    
    proxy:  指代我们所代理的那个真实对象
    method:  指代的是我们所要调用真实对象的某个方法的Method对象
    args:  指代的是调用真实对象某个方法时接受的参数
    
    
    

    理清职责

    • 实现一个带名字的打印机
      名字=====》》说明
      Printer || 表示带名字的打印机的类(本人)
      Printable || Printer和PrinterProxy的共同接口
      PrinterProxy || 表示带名字的打印机的类(代理人)
      Main ||测试程序行为的类

    UML

    类图:

    时序图:

    Code

    • Printable
    
    
    public interface Printable {
    
        // 设置打印名字
        void setPrinterName(String name);
    
        // 获取打印名字
        String getPrinterName();
    
        // 显示文字
        void print(String string);
    }
    
    
    
    • Printer
    
    
    public class Printer implements Printable{
    
    
        private String name;
    
        public Printer() {
            heavyjob("正在生成Printer实例");
        }
    
        public Printer(String name) {
            this.name = name;
            heavyjob("正在生成Printer实例("+name+")");
        }
    
        /**
         * 模拟一个高负载任务
         * @param string
         */
        private void heavyjob(String string) {
            System.out.println(string);
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.print(".");
            }
        }
    
        @Override
        public void setPrinterName(String name) {
            this.name=name;
        }
    
        @Override
        public String getPrinterName() {
            return name;
        }
    
        @Override
        public void print(String string) {
            System.out.println("==="+name+"===");
            System.out.println(string);
        }
    }
    
    
    
    • PrinterProxy
    
    
    public class PrinterProxy implements Printable{
    
        private String name;
    
        private Printer real;
    
        /**
         * 不论 setPrinterName 方法和getPrinterName 方法被调用多少次,
         * 都不会生成Printer类的实例。只有当真正需要本人时,
         * 才会生成printer类的实例(printerProxy类的调用者完全不知道是否生成了本人,也不用在意是否生成了本人)。
         * @param name
         */
    
    
        public PrinterProxy(String name) {
            this.name = name;
        }
    
        @Override
        public synchronized void setPrinterName(String name) {
            if(real!=null){
                real.setPrinterName(name);
            }
            this.name=name;
        }
    
        @Override
        public String getPrinterName() {
            return name;
        }
    
        @Override
        public void print(String string) {
                realize();
                real.print(string);
        }
    
        private synchronized void realize() {
            if(real==null) real=new Printer(name);
        }
    }
    
    public class MainT {
        public static void main(String[] args) {
    
            Printable p=new PrinterProxy("Tom");
            System.out.println("现在是"+p.getPrinterName());
            p.setPrinterName("Cat");
            System.out.println("现在是"+p.getPrinterName());
            p.print("我是 Tomcat");
    
        }
    }
    
    
    • 结果:
    
    
    现在是Tom
    现在是Cat
    正在生成Printer实例(Cat)
    .....
    ===Cat===
    我是 Tomcat
    
    

    动态代理Code

    
    
    public class Client {
    
        public static void main(String[] args) {
    
            Printable tom = new Printer("Tom");
    
            DynamicProxy proxy = new DynamicProxy(tom);
    
            Printable o = (Printable) Proxy.newProxyInstance(proxy.getClass().getClassLoader(),
                    tom.getClass().getInterfaces(), proxy);
    
            System.out.println(o.getClass().getName());
    
            System.out.println("现在是"+o.getPrinterName());
            o.print("Tomcat");
        }
    }
    
    
    
    
    public class DynamicProxy implements InvocationHandler {
    
        private Object object;
    
        public DynamicProxy(Object object) {
            this.object = object;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            System.out.println("准备执行");
    
            System.out.println("Method:" + method);
    
            Object o = method.invoke(object, args);
    
            System.out.println("执行完毕");
            return o;
        }
    }
    
    
    
    
    public interface Printable {
    
        // 设置打印名字
        void setPrinterName(String name);
    
        // 获取打印名字
        String getPrinterName();
    
        // 显示文字
        void print(String string);
    }
    
    
    
    public class Printer implements Printable{
        private String name;
    
        public Printer() {
            heavyjob("正在生成Printer实例");
        }
    
        public Printer(String name) {
            this.name = name;
            heavyjob("正在生成Printer实例("+name+")");
        }
    
        /**
         * 模拟一个高负载任务
         * @param string
         */
        private void heavyjob(String string) {
            System.out.println(string);
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.print(".");
            }
        }
    
        @Override
        public void setPrinterName(String name) {
            this.name=name;
        }
    
        @Override
        public String getPrinterName() {
            return name;
        }
    
        @Override
        public void print(String string) {
            System.out.println("==="+name+"===");
            System.out.println(string);
        }
    }
    
    
    
    • 关于代理Proxy 中proxy参数使用

    1.proxy参数运行时的类型是什么
    proxy的类型是:com.sun.proxy.$Proxy0真实的代理对象

    2.为什么不用this替代
    因为this代表的是InvocationHandler接口实现类本身,并不是真实的代理对象。

    整体代码:

    
    
    
    interface I_Db_Tools{
    	void open();
    	void close();
    	I_Db_Tools exchangeDb(String dbname);
    }
    class Db_Tools implements I_Db_Tools{
    	public void open() {
    		System.out.println("数据库连接..........");
    	}
    	public void close() {
    		System.out.println("数据库关闭..........");
    	}
    	@Override
    	public I_Db_Tools exchangeDb(String db_name) {
    		System.out.println("改变数据为:"+db_name);
    		return this;
    	}
    }
    public class ProxyD {
    	public static void main(String[] args) {
    
    		I_Db_Tools db_Tools = new Db_Tools();
    
    		I_Db_Tools db_tools_proxy = ((I_Db_Tools) Proxy.newProxyInstance(db_Tools.getClass().getClassLoader(),
    				db_Tools.getClass().getInterfaces(), new InvocationHandler() {
    					@Override
    					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    						// 利用proxy实现代理返回同一个对象
    						if ("exchangeDb".equals(method.getName())) {
    							String db_name=(String) args[0];
    							System.out.println("改变数据为:"+db_name);
    							return proxy;
    						}
    						if ("open".equals(method.getName())) {
    							return method.invoke(db_Tools, args);// 代理open方法
    						}
    						return null;//不代理其他方法
    					}
    				}));
    		I_Db_Tools db = db_Tools.exchangeDb("mysql").exchangeDb("Oracle");
    		db_tools_proxy.open();
    		db_tools_proxy.close();
    	}
    }
    
    
    
  • 相关阅读:
    idea 控制到不能输出中文
    后台学习
    carthage和cocoapods
    如何优雅地调试
    从一次内存峰值说起
    多线程单线程,同步异步,并发并行,串行队列并行队列,看这里就对了
    iOS网络层设计感想
    iOS团队风格的统一
    AFNetworking二次封装的那些事
    UITextFiled,UITextView长度限制
  • 原文地址:https://www.cnblogs.com/dgwblog/p/9899023.html
Copyright © 2011-2022 走看看