proxy_proxydroid - CSDN
精华内容
参与话题
  • 代理模式——Proxy

    千次阅读 2018-11-17 17:08:57
    终于静下心来好好做一下代理模式的笔记了。说实话,代理模式这个词对我来说又熟悉又陌生。你说陌生吧,Spring 的AOP也了解过一点;你说熟悉吧,但总感觉抓不住它的尾巴,滑不溜秋,...Proxy的定义代理模式的优点Pro...
    
    

    终于静下心来好好做一下代理模式的笔记了。说实话,代理模式这个词对我来说又熟悉又陌生。你说陌生吧,Spring 的AOP也了解过一点;你说熟悉吧,但总感觉抓不住它的尾巴,滑不溜秋,在脑海里总没有一个确切的概念。拿来《设计模式之禅》一读,还别说,感觉理解那么一点了。所以就七分抄三分悟记一下。

    案例展示——Proxy怎么用?

     代理模式可以说是设计模式界的一个明星模式了,连好多不知道设计模式是什么的程序员或许都听过它的名声,不为其他,就因为它代理这个骚气的功能。一听到代理,就联想到了游戏界大名鼎鼎的代练,想不记住都难,所谓真理源于生活而高于生活,所以为了追寻本源,我们还是从生活中去探索该设计模式吧。

     考虑这样一种场景:随着互联网的发展,游戏联网已经成为一种大趋势,无数“英雄豪杰”在网络游戏中大杀四方,仿佛自己就是无所不能的神。可是,无论是现实世界还是虚拟世界,还是分三六九等的,有的人累死累活还是在低级段位挣扎,时间久了,疲了,倦了,于是感觉生命没有了任何意义。熟不知,现实世界虽然没有造物之手干预命运,可虚拟世界却有很多大神翻云覆雨。于是,你看到了向高级段位奋进的曙光,求大神代打。。。代练就这样应运而生了。废话不多说了,直接上设计类图:

    image

    • IGamePlayer是一个游戏玩家接口,里面定义了在游戏中常用的功能

    • GamePlayer是一个玩家实现类,实现了IGamePlayer接口

    • GamePlayerProxy是一个GamePlaye的代理类,代理执行GamePlayer的方法

    如下是代码实现:

    //游戏玩家接口
    public interface IGamePlayer {
        //登录游戏
        void login(String user, String password);
        //杀老怪
        void killBoss();
        //升级
        void upgrade();
    }
    
    //玩家实现类
    public class GamePlayer implements IGamePlayer{
        //玩家的名字
        private String name = "";
    
        public GamePlayer(String name) {
            this.name = name;
        }
        //登录游戏
        public void login(String user, String password) {
            System.out.println("玩家ID为 " + user + " 的用户正在登录  " + this.name + "登录成功!");
        }
        //杀老怪
        public void killBoss() {
            System.out.println(this.name + " 正在杀老怪!");
        }
        //升级
        public void upgrade() {
            System.out.println(this.name + " 升了一级!");
        }
    }
    
    //代理类
    public class GamePlayerProxy implements IGamePlayer {
        //持有一个IGamePlayer的引用
        private IGamePlayer gamePlayer = null;
        //通过构造函数传递要对谁代练
        public GamePlayerProxy(IGamePlayer gamePlayer) {
            this.gamePlayer = gamePlayer;
        }
        //代练登录
        public void login(String user, String password) {
            this.gamePlayer.login(user, password);
        }
        //代练杀老怪
        public void killBoss() {
            this.gamePlayer.killBoss();
        }
        //代练升级
        public void upgrade() {
            this.gamePlayer.upgrade();
        }
    }
    

    在一个场景中实现得:

    public class Client {
        public static void main(String[] args) {
            //定义一个玩家
            IGamePlayer gamePlayer = new GamePlayer("李大海");
            //定义一个代练者
            IGamePlayer proxy = new GamePlayerProxy(gamePlayer);
            //开始打游戏
            System.out.println("游戏开始时间:2018-9-18 10:00");
            //登录
            proxy.login("沧海一声笑", "password");
            //杀怪
            proxy.killBoss();
            //升级
            proxy.upgrade();
            //记录游戏结束时间
            System.out.println("游戏结束时间:2018-9-18 22:00");
        }
    }
    
    //结果如下:
    游戏开始时间:2018-9-18 10:00
    玩家ID为 沧海一声笑 的用户正在登录  李大海登录成功!
    李大海 正在杀老怪!
    李大海 升了一级!
    游戏结束时间:2018-9-18 22:00
    

    深入分析——Proxy是什么?

    Proxy的定义

    定义: 为其他对象提供一种代理以控制对这个对象的访问。其通用设计类图如下:

    image

    • Subject抽象主题类: 抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求

    • RealSubject具体角色: 也叫做被委托角色,被代理角色。是业务逻辑的具体执行者。

    • Proxy代理主题角色: 也叫做委托类,代理类。它负责对真实角色的应用,将抽象类中定义的方法委托给真实角色实现,自己执行。

    代理模式的优点
    • 职责清晰: 真实角色就是实现实际的业务逻辑,不用关心其他非本职的事物。

    • 高扩展性: 真实角色可以随时更换或扩展,只需要实现接口就行,而代理不需要有任何变化

    Proxy的扩展

    代理有普通代理和强制代理之分。

    1. 普通代理

     普通代理是指客户端只能访问代理角色,不能访问真实角色。什么意思?就好比说你请了一个律师帮你打官司,任何纠纷都由律师帮你摆平,你只要在幕后就行。下面是设计类图:

    image

     延续了前面的案例,只是做了一个很小的改动:分别修改了GamePlayer和GamePlayerProxy的构造函数。在GamePlayer的构造函数中增加一个IGamePlayer的参数,GamePlayerProxy只需要传入被代理者的名字就行。

    代码实现如下(只给出改动部分,其他的与上面的案例相同):

    //玩家
    public class GamePlayer implements IGamePlayer{
        //玩家的名字
        private String name = "";
    
        //构造函数限制谁能创建对象,同时传递姓名
        public GamePlayer(IGamePlayer gamePlayer, String name) throws Exception {
            if (gamePlayer == null) {
                throw new Exception("不能创建真是角色!");
            } else {
                this.name = name;
            }
        }
        
        //登录游戏
        public void login(String user, String password) {
            System.out.println("玩家ID为 " + user + " 的用户正在登录  " + this.name + "登录成功!");
        }
        //杀老怪
        public void killBoss() {
            System.out.println(this.name + " 正在杀老怪!");
        }
        //升级
        public void upgrade() {
            System.out.println(this.name + " 升了一级!");
        }
    }
    
    //代练
    public class GamePlayerProxy implements IGamePlayer {
        //持有一个IGamePlayer的引用
        private IGamePlayer gamePlayer = null;
        //通过构造函数传递要对谁代练
        
        public GamePlayerProxy(String name) {
           try {
               gamePlayer = new GamePlayer(this, name);
           } catch (Exception e) {
               e.printStackTrace();
           }
        }
        
        //代练登录
        public void login(String user, String password) {
            this.gamePlayer.login(user, password);
        }
        //代练杀老怪
        public void killBoss() {
            this.gamePlayer.killBoss();
        }
        //代练升级
        public void upgrade() {
            this.gamePlayer.upgrade();
        }
    }
    
    //在一个场景中运行
    public class Client {
        public static void main(String[] args) {
            //定义一个代练者
            IGamePlayer proxy = new GamePlayerProxy("李大海");
            //开始打游戏
            System.out.println("游戏开始时间:2018-9-18 10:00");
            //登录
            proxy.login("沧海一声笑", "password");
            //杀怪
            proxy.killBoss();
            //升级
            proxy.upgrade();
            //记录游戏结束时间
            System.out.println("游戏结束时间:2018-9-18 22:00");
        }
    }
    
    //结果如下:
    游戏开始时间:2018-9-18 10:00
    玩家ID为 沧海一声笑 的用户正在登录  李大海登录成功!
    李大海 正在杀老怪!
    李大海 升了一级!
    游戏结束时间:2018-9-18 22:00
    

     我们发现,结果没有任何变化。仅仅修改了构造函数,就屏蔽了真实角色对系统的影响:对于一个代练,我不需要知道真实角色的其他情况,你只要告诉告诉我他的名字就行(登录名和密码)。在该模式下,调用者只知道代理而不用知道真是的角色是谁,屏蔽了真实角色的变更对高层模块的影响,该模式很适合对扩展性要求很高的场合。

    2. 强制代理

     强制代理的话和普通代理相反。普通代理是通过代理找到真实角色,而强制代理则是通过真实角色找到自己的代理是谁。就好比说你想和当事人私下和解,不通过他的律师,一打电话约一下,他却说别找他,找他的律师商量,你说气不气人?其实强制代理就是这么回事儿,有事别找我,找我指定的私人代理。下面是设计类图:

    image

     在IGamePlayer中增加了一个getProxy()的方法,用于返回每一个玩家的私人代练。该方法需要每个玩家类自己去实现(注意:代练也可以找代练,本例中代练的代理是自己)。

    下面是代码实现:

    //玩家接口
    public interface IGamePlayer {
        //登录游戏
        void login(String user, String password);
        //杀老怪
        void killBoss();
        //升级
        void upgrade();
        //每个人都可以找自己的代理
        IGamePlayer getProxy();
    }
    
    //玩家
    public class GamePlayer implements IGamePlayer{
        //玩家的名字
        private String name = "";
        //自己的代理
        private IGamePlayer proxy = null;
    
        public GamePlayer(String name){
                this.name = name;
        }
    
        //找到自己的代理
        public IGamePlayer getProxy() {
            this.proxy = new GamePlayerProxy(this);
            return this.proxy;
        }
    
        //登录游戏
        public void login(String user, String password) {
            if (this.isProxy()) {
                System.out.println("玩家ID为 " + user + " 的用户正在登录  " + this.name + "登录成功!");
            } else {
                System.out.println("请使用指定代理访问!");
            }
        }
        //杀老怪
        public void killBoss() {
            if (this.isProxy()) {
                System.out.println(this.name + " 正在杀老怪!");
            } else {
                System.out.println("请使用指定代理访问!");
            }
        }
        //升级
        public void upgrade() {
            if (this.isProxy()) {
                System.out.println(this.name + " 升了一级!");
            } else {
                System.out.println("请使用指定代理访问!");
            }
        }
    
        //校验是否是代理访问
        private boolean isProxy() {
            if (this.proxy == null) {
                return false;
            } else {
                return true;
            }
        }
    }
    
    //代练
    public class GamePlayerProxy implements IGamePlayer {
        //持有一个IGamePlayer的引用
        private IGamePlayer gamePlayer = null;
    
        //通过构造函数传递要对谁代练
        public GamePlayerProxy(IGamePlayer gamePlayer) {
            this.gamePlayer = gamePlayer;
        }
    
        //代练登录
        public void login(String user, String password) {
            this.gamePlayer.login(user, password);
        }
        //代练杀老怪
        public void killBoss() {
            this.gamePlayer.killBoss();
        }
        //代练升级
        public void upgrade() {
            this.gamePlayer.upgrade();
        }
    
        //代练的代理是自己
        public IGamePlayer getProxy() {
            return this;
        }
    }
    
    //在一个场景中运行:
    public class Client {
    
        /*
        public static void main(String[] args) {
            //定义一个玩家
            IGamePlayer gamePlayer = new GamePlayer("李大海");
            //定义一个代练者
            IGamePlayer proxy = new GamePlayerProxy(gamePlayer);
            //开始打游戏
            System.out.println("游戏开始时间:2018-9-18 10:00");
            //登录
            proxy.login("沧海一声笑", "password");
            //杀怪
            proxy.killBoss();
            //升级
            proxy.upgrade();
            //记录游戏结束时间
            System.out.println("游戏结束时间:2018-9-18 22:00");
        }*/
    
        public static void main(String[] args) {
            //定义一个玩家
            IGamePlayer gamePlayer = new GamePlayer("李大海");
            //获得指定的代理
            IGamePlayer proxy = gamePlayer.getProxy();
            //开始打游戏
            System.out.println("游戏开始时间:2018-9-18 10:00");
            //登录
            proxy.login("沧海一声笑", "password");
            //杀怪
            proxy.killBoss();
            //升级
            proxy.upgrade();
            //记录游戏结束时间
            System.out.println("游戏结束时间:2018-9-18 22:00");
        }
    }
    
    //结果如下(不是自己的私人代练不允许访问):
    游戏开始时间:2018-9-18 10:00
    请使用指定代理访问!
    请使用指定代理访问!
    请使用指定代理访问!
    游戏结束时间:2018-9-18 22:00
    
    游戏开始时间:2018-9-18 10:00
    玩家ID为 沧海一声笑 的用户正在登录  李大海登录成功!
    李大海 正在杀老怪!
    李大海 升了一级!
    游戏结束时间:2018-9-18 22:00
    

     强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色。高层模块只要调用getProxy()方法就可以得到真实角色的代理,从而访问自己的所有方法,代理的管理由真实角色自己完成。

    3. 个性代理

     代理不但可以实现主题接口,也可以实现其他接口完成不同的任务。就如一个律师不但可以帮你打官司,也可以帮其他人打官司;代练也可以帮许多人代打。代理的目的是在目标对象方法的基础上进行增强,本质通常就是对目标方法的拦截和过滤,例如游戏代理是需要收费的,这个计算功能就是代理的个性,它应该在代理的接口中定义,类图设计如下:

    image

    代码实现如下(只对修改部分进行展示,其他与案例相同):

    //代理接口
    public interface Proxy {
        //计算费用
        void count();
    }
    
    //代理
    public class GamePlayerProxy implements IGamePlayer, Proxy {
        //持有一个IGamePlayer的引用
        private IGamePlayer gamePlayer = null;
    
        //通过构造函数传递要对谁代练
        public GamePlayerProxy(IGamePlayer gamePlayer) {
            this.gamePlayer = gamePlayer;
        }
    
        //代练登录
        public void login(String user, String password) {
            this.gamePlayer.login(user, password);
        }
        //代练杀老怪
        public void killBoss() {
            this.gamePlayer.killBoss();
        }
        //代练升级
        public void upgrade() {
            this.gamePlayer.upgrade();
            this.count();
        }
    
        //计算费用
        public void count() {
            System.out.println("从青铜到王者总费用:3000元");
        }
    }
    
    //结果如下:
    游戏开始时间:2018-9-18 10:00
    玩家ID为 沧海一声笑 的用户正在登录  李大海登录成功!
    李大海 正在杀老怪!
    李大海 升了一级!
    从青铜到王者总费用:3000元
    游戏结束时间:2018-9-18 22:00
    

    终极Boss——动态代理

     终于到这个老哥出场了,实话说,前面做的那些都是为这哥们出场做铺垫,不为啥,就是因为这老哥太牛逼,谁让他是很多著名框架的宠儿呢(大名鼎鼎的Spring AOP就是基于动态代理实现的)。

     吹捧了一番动态代理,言归正传说说动态代理是什么。其实前面讲的案例归结起来都需要自己写代理类,所以一般叫做静态代理。而动态代理的话是在实现阶段不需要关心代理谁,在运行阶段会动态生成一个代理类去代理指定的对象,类图设计如下:

    image

     增加了一个InvocationHandler接口,该接口是有JDK提供的,所有的动态代理类都需要实现这个接口才能实现JDK的动态代理。GamePlayHandler是一个动态代理的Handler类,负责找到被代理类,并调用被代理类的方法。

    下面是代码实现:

    //动态代理的Handler类
    public class GamePlayerHandler implements InvocationHandler {
        //被代理者
        Class c = null;
        //被代理的实例
        Object obj = null;
        
        //要代理哪个实例
        public GamePlayerHandler(Object obj) {
            this.obj = obj;
        }
        
        //调用被代理类方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = method.invoke(this.obj, args);
            //如果是登录方法,发送一条消息给被代理者,以防被盗号(AOP编程)
            if (method.getName().equalsIgnoreCase("login")) {
                System.out.println("有人在用我的账号登录!");
            }
            return result;
        }
    }
    
    //在一个场景中运行:
    public class Client {
        public static void main(String[] args) {
            //定义一个玩家
            IGamePlayer gamePlayer = new GamePlayer("李大海");
            //定义一个handler
            InvocationHandler handler = new GamePlayerHandler(gamePlayer);
            //获得类的ClassLoader
            ClassLoader cl = gamePlayer.getClass().getClassLoader();
            //动态产生一个代理者
            IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl, new Class[]{IGamePlayer.class}, handler);
    
            //开始打游戏
            System.out.println("游戏开始时间:2018-9-18 10:00");
            //登录
            proxy.login("沧海一声笑", "password");
            //杀怪
            proxy.killBoss();
            //升级
            proxy.upgrade();
            //记录游戏结束时间
            System.out.println("游戏结束时间:2018-9-18 22:00");
        }
    }
    
    //结果如下:
    游戏开始时间:2018-9-18 10:00
    玩家ID为 沧海一声笑 的用户正在登录  李大海登录成功!
    有人在用我的账号登录! //代练登录时发一个通知,防止盗号
    李大海 正在杀老怪!
    李大海 升了一级!
    游戏结束时间:2018-9-18 22:00
    

     初次接触的时候可能会有点懵,没有创建代理类哪里来的代理啊?别急,其实这就是动态代理的精髓:动态代理类是在知道指定的代理对象时动态创建的(代理对象的类加载器显示加载)。在调试分析时会出现类似$Proxy0这样的结构,如下图:

    image

    动态代理模型

     前面提了一个关键名词:AOP(面向切面编程),其核心就是动态代理机制。关于AOP是什么,这里就不做拓展。在项目开发中,对于事物,日志,权限等在系统设计阶段可以不用考虑,而在设计后通过AOP的方式横切过去,并不会影响竖直业务的进行。下面是动态代理的模型:

    image

    其代码实现如下:

    //抽象主题
    public interface Subject {
        //业务操作
        public void doSomething(String str);
    }
    
    //真实主题
    public class RealSubject implements Subject {
        //业务处理
        public void doSomething(String str) {
            System.out.println("搞事情。。。" + str);
        }
    }
    
    //动态代理的Handler类
    public class MyInvocationHandler implements InvocationHandler {
        //被代理的对象
        private Object target = null;
        //通过构造函数传递一个对象
        public MyInvocationHandler(Object obj) {
            this.target = obj;
        }
        //调用被代理类的方法
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(this.target, args);
        }
    }
    
    //动态代理类
    public class DynamicProxy<T> {
        public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
            //找到连接点:方法,属性等
            if (true) {
                //执行一个前置通知
                (new BeforeAdvice()).exec();
            }
            //执行目标并返回结果
            return (T) Proxy.newProxyInstance(loader, interfaces, h);
        }
    }
    
    //具体业务的动态代理
    public class SubjectDynamicProxy extends DynamicProxy {
        public static <T> T newProxyInstance(Subject subject) {
            //获得ClassLoader
            ClassLoader loader = subject.getClass().getClassLoader();
            //获得接口数组
            Class<?>[] classes = subject.getClass().getInterfaces();
            //获得handler
            InvocationHandler handler = new MyInvocationHandler(subject);
            return newProxyInstance(loader, classes, handler);
        }
    }
    
    //通知接口及实现
    public interface Advice {
        //执行
        public void exec();
    }
    //前置通知
    public class BeforeAdvice implements Advice {
        public void exec() {
            System.out.println("前置通知被执行。。。");
        }
    }
    
    //在一个场景中运行:
    public class Client {
        public static void main(String[] args) {
            //定义一个主题
            Subject subject = new RealSubject();
            //定义subject的代理
            Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);
            //代理行为
            proxy.doSomething("开除你!");
        }
    }
    
    //结果如下:
    前置通知被执行。。。
    搞事情。。。开除你!
    

     看,在执行真正的业务方法之前,插入了一个前置通知方法,永远会在业务方法之前发送一个通知,这就是横切面编程:在不改变我们已有代码结构的情况下增强或控制对象的行为。 这里需要注意一点:实现JDK的动态代理,被代理类必须实现一个接口(其他的动态代理技术如CGLIB可以没有接口)。下面是关于动态代理模型的一个调用时序图:

    image

    参考

    《设计模式之禅》

    展开全文
  • 什么是 Proxy

    2019-08-05 11:58:07
    什么是 Proxy 通常,当谈到JavaScript语言时,我们讨论的是ES6标准提供的新特性,本文也不例外。 我们将讨论JavaScript代理以及它们的作用,但在我们深入研究之前,我们先来看一下Proxy的定义是什么。 MDN上的定义...

    这是在浏览其他博客看到的有营养的文章分享给大家。

    什么是 Proxy

    通常,当谈到JavaScript语言时,我们讨论的是ES6标准提供的新特性,本文也不例外。 我们将讨论JavaScript代理以及它们的作用,但在我们深入研究之前,我们先来看一下Proxy的定义是什么。

    MDN上的定义是:代理对象是用于定义基本操作的自定义行为(例如,属性查找,赋值,枚举,函数调用等)。

    换句话说,我们可以说代理对象是我们的目标对象的包装器,我们可以在其中操纵其属性并阻止对它的直接访问。 你可能会发现将它们应用到实际代码中很困难,我鼓励你仔细阅读这个概念,它可能会改变你的观点。

    术语

    handler

    包含陷阱(traps)的占位符对象。

    traps

    提供属性访问的方法。这类似于操作系统中捕获器的概念。

    target

    代理虚拟化的对象。(由代理对象包装和操作的实际对象)

    在本文中,我将为 getset 陷阱 提供简单的用例,考虑到最后,我们将看到如何使用它们并包含更复杂的功能,如API。

    语法和用例

    let p = new Proxy(target, handler);
    复制代码

    将目标和处理程序传递给Proxy构造函数,这样就创建了一个proxy对象。现在,让我们看看如何利用它。为了更清楚地看出Proxy的好处,首先,我们需要编写一些没有它的代码。

    想象一下,我们有一个带有几个属性的用户对象,如果属性存在,我们想要打印用户信息,如果不存在,则抛出异常。在不使用代理对象时,判断属性值是否存在的代码也放在了打印用户信息的函数,即 printUser 中(这并不是我们所希望的),如下demo所示:

    let user = {
        name: 'John',
        surname: 'Doe'
    };
    
    let printUser = (property) => {
        let value = user[property];
        if (!value) {
            throw new Error(`The property [${property}] does not exist`);
        } else {
            console.log(`The user ${property} is ${value}`);
        }
    }
    
    printUser('name'); // 输出: 'The user name is John'
    printUser('email'); // 抛出错误: The property [email] does not exist
    复制代码

    get

    通过查看上面的代码,你会发现:将条件和异常移到其他地方,而printUser中仅关注显示用户信息的实际逻辑会更好。这是我们可以使用代理对象的地方,让我们更新一下这个例子。

    let user = {
        name: 'John',
        surname: 'Doe'
    };
    
    let proxy = new Proxy(user, {
        get(target, property) {
            let value = target[property];
            if (!value) {
                throw new Error(`The property [${property}] does not exist`);
            }
            return value;
        }
    });
    
    let printUser = (property) => {
        console.log(`The user ${property} is ${proxy[property]}`);
    };
    
    printUser('name'); // 输出: 'The user name is John'
    printUser('email'); // 抛出错误: The property [email] does not exist
    复制代码

    在上面的示例中,我们包装了 user 对象,并设置了一个 get 方法。 此方法充当拦截器,在返回值之前,会首先对属性值进行检查,如果不存在,则抛出异常。

    输出与第一种情况相同,但此时 printUser 函数专注于逻辑,只处理消息。

    set

    代理可能有用的另一个例子是属性值验证。在这种情况下,我们需要使用 set 方法,并在其中进行验证。例如,当我们需要确保目标类型时,这是一个非常有用的钩子。我们来看一下实际使用:

    let user = new Proxy({}, {
        set(target, property, value) {
            if (property === 'name' && Object.prototype.toString.call(value) !== '[object String]') { // 确保是 string 类型
                throw new Error(`The value for [${property}] must be a string`);
            };
            target[property] = value;
        }
    });
    
    user.name = 1; // 抛出错误: The value for [name] must be a string
    复制代码

    这些是相当简单的用例,以下场景,proxy均可以派上用场:

    • 格式化
    • 价值和类型修正
    • 数据绑定
    • 调试
    • ...

    现在是时候创建一个更复杂的用例了。

    具有代理的API - 更复杂的示例

    通过使用简单用例中的知识,我们可以创建一个API包装器,以便在我们的应用程序中使用。 当前只支持 getpost 请求,但它可以很容易地扩展。代码如下所示。

    const api = new Proxy({}, {
        get(target, key, context) {
            return target[key] || ['get', 'post'].reduce((acc, key) => {
                acc[key] = (config, data) => {
    
                    if (!config && !config.url || config.url === '') throw new Error('Url cannot be empty.');
                    let isPost = key === 'post';
    
                    if (isPost && !data) throw new Error('Please provide data in JSON format when using POST request.');
    
                    config.headers = isPost ? Object.assign(config.headers || {}, { 'content-type': 'application/json;chartset=utf8' }) :
                        config.headers;
    
                    return new Promise((resolve, reject) => {
                        let xhr = new XMLHttpRequest();
                        xhr.open(key, config.url);
                        if (config.headers) {
                            Object.keys(config.headers).forEach((header) => {
                                xhr.setRequestHeader(header, config.headers[header]);
                            });
                        }
                        xhr.onload = () => (xhr.status === 200 ? resolve : reject)(xhr);
                        xhr.onerror = () => reject(xhr);
                        xhr.send(isPost ? JSON.stringify(data) : null);
                    });
                };
                return acc;
            }, target)[key];
        },
        set() {
            throw new Error('API methods are readonly');
        },
        deleteProperty() {
            throw new Error('API methods cannot be deleted!');
        }
    });
    复制代码

    让我们解释一下简单实现,setdeleteProperty。 我们添加了一个保护级别,并确保每当有人意外或无意地尝试为任何API属性设置新值时,都会抛出异常。

    每次尝试删除属性时都会调用 deleteProperty 方法。可以确保没有人可以从我们的代理(即此处的 api)中删除任何属性,因为通常我们都不想丢失API方法。

    get 在这里很有趣,它做了几件事。target 是一个空对象,get 方法将在第一次有人使用 api 时创建所有方法(如当前的 getpost请求),在 reduce 回调中,我们根据提供的配置执行API规范所需的验证和检查。在此示例中,我们不允许空URL和发布请求而不提供数据。这些检查可以扩展和修改,但重要的是我们只能在这一个地方集中处理。

    reduce 仅在第一次API调用时完成,之后都会跳过整个 reduce 进程,get 只会执行默认行为并返回属性值,即API处理程序。每个处理程序返回一个Promise对象,负责创建请求并调用服务。

    使用:

    api.get({
        url: 'my-url'
    }).then((xhr) => {
        alert('Success');
    }, (xhr) => {
        alert('Fail');
    });
    复制代码
    delete api.get; //throw new Error('API methods cannot be deleted!'); 
    复制代码

    结论

    当您需要对数据进行更多控制时,代理可以派上用场。你可以根据受控规则扩展或拒绝对原始数据的访问,从而监视对象并确保正确行为。

     

    展开全文
  • ES6 -- Proxy代理

    万次阅读 2018-09-13 11:29:18
    Proxy用于修改某些操作的默认行为,等同与在语言层面做出修改,即对编程语言进行编程。 Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以...

    Proxy用于修改某些操作的默认行为,等同与在语言层面做出修改,即对编程语言进行编程。

    Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

     

    它实现的就是“方法无法找到时”的行为,Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。

    handler

    包含陷阱(traps)的占位符对象。

    traps

    提供属性访问的方法。这类似于操作系统中陷阱的概念。

    target

    代理虚拟化的对象。它通常用作代理的存储后端。根据目标验证关于对象不可扩展性或不可配置属性的不变量(保持不变的语义)。

     

    语法:let p = new Proxy(target, handler);

    target

    Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

    handler

    一个对象,其属性是当执行一个操作时定义代理的行为的函数

     

    展开全文
  • 代理模式(proxy

    千次阅读 2018-08-29 11:08:12
    前言 代理模式是一个大类,而且会经常用到,它包含了远程代理,虚拟代理,防火墙代理等,当然还有动态代理了,学过spring的人应该不陌生。 各种代理模式样式差别很大,不容易从程序上辨认,但是可以从功能上认...

    前言

      • 代理模式是一个大类,而且会经常用到,它包含了远程代理,虚拟代理,防火墙代理等,当然还有动态代理了,学过spring的人应该不陌生。
      • 各种代理模式样式差别很大,不容易从程序上辨认,但是可以从功能上认出来,今天我就举个例子聊聊代理模式最基本的样子,从例子中认识代理模式。
      • 举例为静态代理的基本应用,稍后再介绍代理模式的一些特点。

     

    情境引入

           本次我们以滴滴为例,滴滴在8.23接到乘客的投诉电话,因此是可以接受投诉的,只不过投诉处理不及时,而8.24日,警方打电话给滴滴客服,滴滴客服却没有权限查询犯罪嫌疑人的出车记录,说明滴滴客服只是滴滴的一个代理,而有权限的做查询的则是滴滴公司。因此本本例中滴滴客服作为代理,代理滴滴公司。

     

    角色组成(代理模式的基本组成)

    抽象主题角色

    一个抽象接口(滴滴)

    具体主题角色

    需要被代理的对象(滴滴公司)

    代理角色

    对象的代理(滴滴客服)

     

    程序类图

     

    java源程序

    滴滴接口

    package proxy_08;
    
    public interface Didi {
    	public void complain();//可以投诉
    	public void queryCarRecord();//可以查询出车记录
    }
    

    滴滴公司(主题)

    package proxy_08;
    
    public class DidiCompany implements Didi{
    
    	public void complain() {
    		System.out.println("收到投诉,请耐心等待处理结果");
    	}
    
    	public void queryCarRecord() {
    		System.out.println("正在查询出车记录,请耐心等待");
    	}
    
    }
    

    滴滴客服(代理)

    package proxy_08;
    
    public class DidiCallCenterProxy implements Didi{
    	DidiCompany didi;
    	public DidiCallCenterProxy(DidiCompany didi) {
    		this.didi=didi;
    	}
    	public void complain() {
    		didi.complain();//调用主题的方法
    	}
    
    	public void queryCarRecord() {
    		System.out.println("不好意思,一线客服没有权限,请去总公司查询");
    	}
    
    }
    

    客户端

    package proxy_08;
    
    public class Client {
    	public static void main(String[] args) {
             	DidiCompany dc=new DidiCompany();
     		Didi didi=new DidiCallCenterProxy(dc);//生成代理对象
    		didi.complain();//投诉
     		didi.queryCarRecord();//查询出车记录
    	}
    }
    

    运行结果

         

     

    实例分析

           从举例中我们可以看出 ,由于代理与被代理实现了相同的接口,因此代理模式可以代理对象的方法,而代理模式的一个用处也就显而易见了,代理模式可以有选择的暴露对象的接口,而对访问者屏蔽一些接口,这样就实现了对对象的控制访问。当然代理模式用途细分就比较多了,但核心都完成了对被代理对象的控制访问。

    现在我们在重新看代理模式 就应该清楚多了

     

    定义

    代理模式为对象提供一个替身或者占位符以控制对这个对象的访问

     

    应用场景

    1. 应用远程代理控制访问远程对象
    2. 虚拟代理控制访问创建开销大的对象
    3. 保护代理基于权限控制对资源的访问
    4. 动态代理去看aop就行了

     

    代理模式优点

      • 实现了访问者与访问对象之间的解耦
      • 代理模式在客户端与对象之间起到中介作用,保护了对对象的访问
      • 代理模式可以在访问过程中增加逻辑(aop)

     

    缺点

           增加代理会使程序请求处理变慢

     

    代理模式与装饰者模式对比

        看类图就知道代理模式和装饰者模式长的是真像,但是说区分他们也容易

    差别

    代理模式

    装饰者模式

    相同点

    都有统一的接口

     

    相同点

    代理模式和对象都实现了接口

    装饰者和对象也实现了接口

    相同点

    都完成了对对象的包装

     

    不同

    代理包装对象是为了控制对对象的访问

    装饰者是为了给对象添加行为

     

    运用简单工厂

           由于对象的生成放在了客户端,我们可以轻易看出运用了代理模式,现在我们用简单工厂封装一下对象的创建,在看看客户端有什么变化,还能不能认出来应用了代理模式呢?

     

    引入简单工厂

    package proxy_08;
    
    public class DidiFactory {
    	Didi didi;
    	public Didi getInstance(){
    		didi=new DidiCallCenterProxy(new DidiCompany());//建立代理对象
    		return didi;
    	}
    }
    

    修改客户端(别的类不用动)

    package proxy_08;
    
    public class Client {
    	public static void main(String[] args) {
    		DidiFactory df=new DidiFactory();
    		Didi didi=df.getInstance();
    		didi.complain();
    		didi.queryCarRecord();
    	}
    }
    

     

    展开全文
  • proxy的理解及其常见用法

    千次阅读 2019-06-21 09:45:13
      虽然以前看过proxy的相关文章,但是最近在看到某为大神用proxy实现了单例及数据双向绑定,所以决定再次好好的来了解一下proxy的用法,谨以此文来记录我对它的理解及用法。 1、什么是Proxy?   Proxy 用于修改...
  • 代理-proxy

    2019-11-03 19:50:12
    瓜子二手车,没有中间商赚差价。市场需求决定商业模式。 代理是多么痛的领悟,但是我们确实基本没有用到,只是我们用的框架肯定一直在...package com.wht.proxy; import java.lang.reflect.InvocationHandler; impo...
  • proxy

    2020-07-30 23:31:44
    NULL 博文链接:https://qgl.iteye.com/blog/2390469
  • Proxy代理的作用

    千次阅读 2019-06-08 19:21:21
    Proxy代理的作用: Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。 Proxy可以理解成,在目标对象之前架设一层“拦截”,...
  • 在我的Android体系架构解读一文中,在kernel层有很多个驱动,Android Logger,Shared Memory Driver,Binder Driver是非常通用非常重要的几个。 其中Binder Driver是Android Framework IPC机制的核心,来学习一下吧
  • 目录 1.地址: 2.将此文件下载下来 3. 重命名为zip文件,将crx-->改为zip 4. 然后解压出来 5. 打开谷歌浏览器的扩展程序 6. 打开开发者模式,加载已解压程序 7. 加载后结果 8. 设定端口 ...2....
  • // let p = new Proxy(target,handel) // tearget: 用Proxy包装的目标对象 // handel:一个对象proxy代理操作的对象 // ----------get用于对象属性读取的拦截 可以接收三个参数 目标对象 属性名 proxy实例本身(可选)-...
  • nginx 之 proxy_pass详解

    万次阅读 2019-08-21 11:05:41
    在nginx中配置proxy_pass代理转发时,如果在proxy_pass后面的url加/,表示绝对根路径;如果没有/,表示相对路径,把匹配的路径部分也给代理走。 假设下面四种情况分别用 http://192.168.1.1/proxy/test.html 进行...
  • es6 javascript的Proxy 实例的方法

    千次阅读 2018-03-21 09:16:46
    1 get()get方法用于拦截某个属性的读取操作。 上文已经有一个例子, 下面是另一个拦截...var proxy = new Proxy(person, { get: function(target, property) { if(property in target) { return target[pro...
  • scrapy配置proxy代理

    万次阅读 2018-05-09 10:36:39
    一、IP池 IP可以从这个几个网站获取: ...如果出现像下面这种提示:”由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败”或者是这种,” 由 于目标计算机积极拒绝,无法连接。...
  • [原创] MySQL Proxy 学习笔记

    万次阅读 2008-01-29 10:37:00
    [原创] MySQL Proxy 学习笔记作者:heiyeluren时间:2008-1-28博客:http://blog.csdn.net/heiyeshuwu 【 测试平台 】服务器端:OS:FreeBSD 6.2Lua: 5.1.1MySQL Server:4.1.22-logMySQL Proxy: 0.6.0客户端:OS...
  • 深度揭秘ES6代理Proxy

    千次阅读 2017-05-02 13:56:11
    最近在博客上看到关于ES6代理的文章都是一些关于如何使用Proxy的例子,很少有说明Proxy原理的文章,要知道只有真正掌握了一项技术的原理,才能够写出精妙绝伦的代码,所以我觉得有必要写一篇关于深刻揭露ES6 Proxy的...
  • CSAPP: Proxy lab

    万次阅读 2017-12-28 12:47:46
    CSAPP proxy lab
  • 背景这个错误是我在使用AOP动态切换数据库,实现数据库的读写分离的时候出现的问题,使用到的系统环境是:<spring.version>3.2.6.RELEASE <mybatis.version>3.2.4 <mybatis-spring.version>1.1.1使用的代码执行切
  • Proxy error:couldnot proxy request

    万次阅读 2019-10-18 09:28:19
    proxy error问题是因为端口号被占。前端出现了500问题,说明后端服务器有问题,并不是前端vue.config中的proxy出现了问题。那么就去检查后端的端口号是否和前端一致。 后端: 这里调用的是测试环境。但前端启动的...
  • java动态代理Proxy.newProxyInstance

    万次阅读 多人点赞 2020-03-10 23:30:49
    动态代理(dynamic proxy) 利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象),代理的是接口(Interfaces),不是类(Class),也不是抽象类。在...
1 2 3 4 5 ... 20
收藏数 634,191
精华内容 253,676
关键字:

proxy