设计模式之禅【代理模式】

真刀实枪之代理模式

  • 我是游戏至尊

    • “最近几年王者荣耀的热度飙升,自己打时可以体验到其中的升级乐趣,但是时间过得很快啊!自己不想打,找代练,好主意!”
    • 作为一名程序员,先将打游戏这段过程系统化一下
    • 代码

      • IGamePlayer

        package com.peng.game;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public interface IGamePlayer {
            // 登录系统
            public void login(String username, String password);
        
            // 杀怪
            public void killBoss();
        
            // 升级
            public void upgrade();
        }
        
      • GamePlayer

        package com.peng.game;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public class GamePlayer implements IGamePlayer {
            private String username;
            private String password;
        
            public GamePlayer(String username, String password) {
                super();
                this.username = username;
                this.password = password;
            }
        
            public String getUsername() {
                return username;
            }
        
            public void setUsername(String username) {
                this.username = username;
            }
        
            public String getPassword() {
                return password;
            }
        
            public void setPassword(String password) {
                this.password = password;
            }
        
            @Override
            public void login(String username, String password) {
                System.out.println(username + "登录了!");
            }
        
            @Override
            public void killBoss() {
                System.out.println("在打怪!");
            }
        
            @Override
            public void upgrade() {
                System.out.println("又升一级!");
            }
        
        }
        
      • Client

        package com.peng.game;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public class Client {
            public static void main(String[] args) {
                // 创建游戏玩家
                IGamePlayer igp = new GamePlayer("123", "***");
                // 登录游戏系统
                igp.login("123", "***");
                // 杀野怪
                igp.killBoss();
                // 升级
                igp.upgrade();
            }
        }
        
    • 心里学家告诉我们:人类对于困难的记忆比对喜悦的记忆要深刻,但是人类对于喜悦是“趋利”性的,每个人都是想Happy的,都不想让苦难靠近,要都不想让苦难靠近,要想获得幸福,苦难也是在所难免的。
    • 游戏玩的时间长了,腰酸背痛眼睛干涩,手臂酸麻。其结果就像吃了“一日丧命散”,静脉逆流,胡思乱想,而走火入魔,那该怎们解决呢,我们也想玩游戏,但又不想碰触到游戏的烦恼。如何解决,代练公司这么多,哈哈,这么好,先修改下类图。

      • 代码

        • GamePlayerProxy

          package com.peng.game;
          
          /**
           * @author kungfu~peng
           * @data 2017年11月17日
           * @description
           */
          public class GamePlayerProxy implements IGamePlayer {
              private IGamePlayer gamePlayer = null;
          
              // 通过构造函数对谁进行代练
              public GamePlayerProxy(IGamePlayer _gamePlayer) {
                  this.gamePlayer = _gamePlayer;
              }
          
              // 代练登录
              @Override
              public void login(String username, String password) {
                  this.gamePlayer.login(username, password);
              }
          
              // 代练打怪
              @Override
              public void killBoss() {
                  this.gamePlayer.killBoss();
              }
          
              // 代练升级
          
              @Override
              public void upgrade() {
                  this.gamePlayer.upgrade();
              }
          
          }
          
        • Client

          package com.peng.game;
          
          /**
           * @author kungfu~peng
           * @data 2017年11月17日
           * @description
           */
          public class Client {
              public static void main(String[] args) {
                  // 创建游戏玩家
                  GamePlayerProxy gpp = new GamePlayerProxy(new GamePlayer("123", "***"));
                  // 登录游戏系统
                  gpp.login("123", "***");
                  // 杀野怪
                  gpp.killBoss();
                  // 升级
                  gpp.upgrade();
              }
          }
          
    • 现在有人帮你打游戏了,终于升到30级,至尊星耀,啊哈哈,现在来看看具体的代理模式吧!

代理模式的定义

  • Proxy Pattern
  • Provide a surrogate or placeholder for another object to control access to it.(为了其他对象提供一种代理以控制对这个对象的访问!)
  • 通用类图
  • 代理模式也叫委托模式,它是一项基本的设计技巧,许多其他的模式,如状态模式、策略模式、访问者模式在本质上是在你更特殊的场合采取了委托模式,而且在日常的应用中,代理模式可以提供更好的访问控制。(Struts2的Form元素映射就是采用了代理模式:动态代理模式)
  • 类图角色
    1. Subject抽象主题角色:可以是接口或者是抽象类
    2. RealSubject具体主题角色:也被叫做委托角色、被代理角色。他才是冤大头,是业务逻辑的具体执行者
    3. Proxy代理主题角色:也叫做委托类、代理类,他负责对真实角色的应用,把所有的抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕后做预处理和善后处理。
  • 代码

    • 抽象主题类

      public interface Subject {
          // 定义一个方法
          public void request();
      }
      
    • 真实主题类

      public class RealSubject implements Subject {
      
          @Override
          public void request() {
              // 业务逻辑处理
          }
      }
      
    • 代理类

      public class Proxy implements Subject {
          // 要代理哪个实现类
          private Subject subject = null;
      
          // 默认被代理者
          public Proxy() {
              this.subject = new Proxy();
          }
      
          // 传递被代理者
          public Proxy(Object... object) {
              //这里自己悟一下--简单提示,自己拓展
              //public Proxy(RealSubject subject) {
              // this.subject=subject;
              //}
          }
      
          @Override
          public void request() {
              this.before();
              this.request();
              this.after();
          }
      
          private void before() {
              // 预处理操作
          }
      
          private void after() {
              // 善后处理
          }
      
      }
      
    • 看到这里先别惊讶为啥还有before方法和after方法。
      • 一个代理类可以代理多个被委托者或被代理者,因此一个代理类具体代表哪个真实主题角色是由场景类来决定。当然,最简单的代理模式是一个主题类和一个代理类。通常情况下,一个接口只需要一个代理类就可以了,具体代理哪个实现类由高层模块来决定,也就是在代理类的构造函数中传递被代理者。

代理模式的应用

  • 代理模式的优点
    1. 职责清晰
    2. 高扩展性
    3. 智能化(提前了解一下:动态代理)
  • 代理模式应用场景
    • 现实世界中出现了一种律师:你不想参与是是非非,只要完成自己的答辩就可以,比如事前调查,事后追查不用你管,律师就可以做了。Spring AOP是一个非常典型的动态代理。

代理模式的拓展

  • 网络上代理服务器分为透明代理和普通代理,那是什么意思呢?
    • 透明代理服务器:用户不用设置代理服务器地址,就可以直接访问,也就是服务器对用户来说是透明的,不用知道它的存在
    • 普通代理服务器:需要用户自己设置服务器的IP地址,用户必须知道代理的存在
    • 强制代理:调用者直接调用真实角色,而不用关心代理是真实存在,其代理的产生是由真实角色决定的
  • 看下不同代理模式的类图吧

    • 普通代理

      • 改动很小,仅仅是修改了两个实现类的构造函数,GamePlayer增加了一个_gamePlayer的参数,而代理模式则传入代理者的名称即可

        • GamePlayer

          package com.peng.game;
          
          /**
           * @author kungfu~peng
           * @data 2017年11月17日
           * @description
           */
          public class GamePlayer implements IGamePlayer {
              private String name="";
              //_gamePlayer放代理者
              public GamePlayer(IGamePlayer _gamePlayer,String _name) throws Exception {
                  super();
                  if(_gamePlayer==null){
                      throw new Exception("不能创建真实角色!");
                  }else{
                      this.name=_name;
                  }
              }
          
              @Override
              public void login(String username, String password) {
                  System.out.println(username + "登录了!");
              }
          
              @Override
              public void killBoss() {
                  System.out.println(this.name+"在打怪!");
              }
          
              @Override
              public void upgrade() {
                  System.out.println(this.name+"又升一级!");
              }
          
          }   
          
        • GamePlayerProxy

          package com.peng.game;
          
          /**
           * @author kungfu~peng
           * @data 2017年11月17日
           * @description
           */
          public class GamePlayerProxy implements IGamePlayer {
              private IGamePlayer gamePlayer = null;
          
              // 通过构造函数对谁进行代练
              public GamePlayerProxy(String name) {
                  try{
                      gamePlayer=new GamePlayer(this,name);
                  }catch(){
                      //异常处理
                  }
              }
          
              // 代练登录
              @Override
              public void login(String username, String password) {
                  this.gamePlayer.login(username, password);
              }
          
              // 代练打怪
              @Override
              public void killBoss() {
                  this.gamePlayer.killBoss();
              }
          
              // 代练升级
          
              @Override
              public void upgrade() {
                  this.gamePlayer.upgrade();
              }
          
          }
          
        • 场景类

          package com.peng.game;
          
          /**
           * @author kungfu~peng
           * @data 2017年11月17日
           * @description
           */
          public class Client {
              public static void main(String[] args) {
                  // 创建游戏玩家
                  GamePlayerProxy gpp = new GamePlayerProxy(new GamePlayer("kungfu~peng"));
                  // 登录游戏系统
                  gpp.login("123", "***");
                  // 杀野怪
                  gpp.killBoss();
                  // 升级
                  gpp.upgrade();
              }
          }
          
        • 在该模式下,调用者只知道代理而不用真实的角色是谁,屏蔽了真实角色的变更对高层对象的影响

    • 强制代理

      • 通过真实角色找到代理,只有通过真实角色指定的代理才可以用。即创建真实角色的时候却创建了个代理出来。
        • IGamePlayer

          package com.peng.game;
          
          /**
           * @author kungfu~peng
           * @data 2017年11月17日
           * @description
           */
          public interface IGamePlayer {
              // 登录系统
              public void login(String username, String password);
          
              // 杀怪
              public void killBoss();
          
              // 升级
              public void upgrade();
          
              // 返回代理
              public IGamePlayer getProxy();
          }
          
        • GamePlayer

          package com.peng.game;
          
          /**
           * @author kungfu~peng
           * @data 2017年11月17日
           * @description
           */
          public class GamePlayer implements IGamePlayer {
              private String name = "";
              private IGamePlayer proxy = null;
          
              public GamePlayer(String _name) throws Exception {
                  super();
                  this.name = _name;
              }
          
              @Override
              public void login(String username, String password) {
                  if (isProxy()) {
                      System.out.println(username + "登录了!");
                  } else {
                      System.out.println("请使用指定的代理访问!");
                  }
              }
          
              @Override
              public void killBoss() {
                  if (isProxy()) {
                      System.out.println(this.name + "在打怪!");
                  } else {
                      System.out.println("请使用指定的代理访问!");
                  }
              }
          
              @Override
              public void upgrade() {
                  if (isProxy()) {
                      System.out.println(this.name + "又升一级!");
                  } else {
                      System.out.println("请使用指定的代理访问!");
                  }
              }
          
              @Override
              public IGamePlayer getProxy() {
                  this.proxy = new GamePlayerProxy(this);
                  return this.proxy;
              }
          
              // 检验代理是否被访问
              private boolean isProxy() {
                  if (this.proxy == null) {
                      return false;
                  } else {
                      return true;
                  }
              }
          }
          
        • GamePlayerProxy

          package com.peng.game;
          
          /**
           * @author kungfu~peng
           * @data 2017年11月17日
           * @description
           */
          public class GamePlayerProxy implements IGamePlayer {
              private IGamePlayer gamePlayer = null;
          
              // 通过构造函数对谁进行代练
              public GamePlayerProxy(IGamePlayer _gamePlayer) {
                  this.gamePlayer = _gamePlayer;
              }
          
              // 代练登录
              @Override
              public void login(String username, String password) {
                  this.gamePlayer.login(username, password);
              }
          
              // 代练打怪
              @Override
              public void killBoss() {
                  this.gamePlayer.killBoss();
              }
          
              // 代练升级
          
              @Override
              public void upgrade() {
                  this.gamePlayer.upgrade();
              }
          
              @Override
              public IGamePlayer getProxy() {
                  return this;
              }
          
          }
          
        • Client(测试1)

          package com.peng.game;
          
          /**
           * @author kungfu~peng
           * @data 2017年11月17日
           * @description
           */
          public class Client {
              public static void main(String[] args) throws Exception {
                  // 创建游戏玩家
                  IGamePlayer gpp =new GamePlayer("kungfu~peng");
                  // 登录游戏系统
                  gpp.login("123", "***");
                  // 杀野怪
                  gpp.killBoss();
                  // 升级
                  gpp.upgrade();
              }
          }
          
          
          //结果:
          请使用指定的代理访问!
          请使用指定的代理访问!
          请使用指定的代理访问!
          
        • Client(测试2)

          package com.peng.game;
          
          /**
           * @author kungfu~peng
           * @data 2017年11月17日
           * @description
           */
          public class Client {
              public static void main(String[] args) throws Exception {
                  // 创建游戏玩家
                  IGamePlayer gpp =new GamePlayer("kungfu~peng");
                  //代练者
                  IGamePlayer proxy =new GamePlayerProxy(gpp);
                  // 登录游戏系统
                  proxy.login("123", "***");
                  // 杀野怪
                  proxy.killBoss();
                  // 升级
                  proxy.upgrade();
              }
          }
          
          //结果:
          请使用指定的代理访问!
          请使用指定的代理访问!
          请使用指定的代理访问!
          
        • Client(测试3)

          package com.peng.game;
          
          /**
           * @author kungfu~peng
           * @data 2017年11月17日
           * @description
           */
          public class Client {
              public static void main(String[] args) throws Exception {
                  // 创建游戏玩家
                  IGamePlayer gpp =new GamePlayer("kungfu~peng");
                  //代练者
                  IGamePlayer proxy =gpp.getProxy();// 自己指定代理者
                  // 登录游戏系统
                  proxy.login("123", "***");
                  // 杀野怪
                  proxy.killBoss();
                  // 升级
                  proxy.upgrade();
              }
          }
          
          
          //结果:
          kungfu~peng登录了!
          kungfu~peng在打怪!
          kungfu~peng又升一级!        
          
      • 类图

        • 强制代理的概念就是要从真正角色查找到代理角色,不允许直接访问真实访问真实角色。高层模块只要调用getProxy()就可以访问真实角色的所有方法,它根本就不需要产生一个代理类出来,代理类管理已经由真实角色自己完成

代理类是有个性的

  • 代理模式不仅仅可以实现主题接口,也可以实现主题接口所没有的方法,而代理的目的性就是在主题接口的同时增强其作用,这种增强的本质就是对目标方法的拦截和过滤
  • 例如代练打游戏要收费,升一级要5元

    • 类图
    • 改动的类

      • IProxy

        package com.peng.game;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public interface IProxy {
            // 计算费用
            public void count();
        }
        
      • GamePlayProxy

        package com.peng.game;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public class GamePlayerProxy implements IGamePlayer, IProxy {
            private IGamePlayer gamePlayer = null;
        
            // 通过构造函数对谁进行代练
            public GamePlayerProxy(IGamePlayer _gamePlayer) {
                this.gamePlayer = _gamePlayer;
            }
        
            // 代练登录
            @Override
            public void login(String username, String password) {
                this.gamePlayer.login(username, password);
            }
        
            // 代练打怪
            @Override
            public void killBoss() {
                this.gamePlayer.killBoss();
            }
        
            // 代练升级
        
            @Override
            public void upgrade() {
                this.gamePlayer.upgrade();
                this.count();
            }
        
            @Override
            public IGamePlayer getProxy() {
                return this;
            }
        
            @Override
            public void count() {
                System.out.println("消费5元!");
            }
        
        }
        
      • 执行效果
        kungfu~peng登录了!
        kungfu~peng在打怪!
        kungfu~peng又升一级!
        消费5元!
        

动态代理

  • 在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。
  • 面向切面编程--AOP--Aspect Oriented Programming
  • 类图

    • 在类中增加了一个InvocationHandler接口和GamePlayIH类,作用就是生成一个对象的代理对象,其中的InvocationHandler是JDK提供的动态代理接口,对被代理类的方法进行代理
    • 代码

      • InvocationHandler:JDK自带的接口,这个不用手动写
      • GamePlayIH

        package com.peng.game;
        
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public class GamePlayIH implements InvocationHandler {
        
            // 被代理者
            Class cls = null;//感觉这个多余...
            // 被代理的实例
            Object obj = null;
        
            // 我要代理谁
            public GamePlayIH(Object obj) {
                super();
                this.obj = obj;
            }
        
            // 调用被代理的方法
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                Object result = method.invoke(this.obj, args);
                return result;
            }
        
        }
        
      • 解释:invoke是接口InvocationHandler定义必须实现的方法,它完成对真实方法的调用。动态代理会宣称“我们已经实现了该接口的所有方法”,动态代理怎样才能实现被代理接口中的方法呢?
        • 默认情况下所有的方法返回值都是空的,使得,代理已经实现它了,但是没有任何的逻辑含义,怎么办?
          • 好办,通过InvocationHandler接口,所有的方法都由Handler来处理,即所有被代理的方法都由InvocationHandler接管实际的处理任务
      • IGamePlayer

        package com.peng.game;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public interface IGamePlayer {
            // 登录系统
            public void login(String username, String password);
        
            // 杀怪
            public void killBoss();
        
            // 升级
            public void upgrade();
        
            // 返回代理
            public IGamePlayer getProxy();
        }
        
      • GamePlayer

        package com.peng.game;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public class GamePlayer implements IGamePlayer {
            private String name = "";
            private IGamePlayer proxy = null;
        
            public GamePlayer(String _name) throws Exception {
                super();
                this.name = _name;
            }
        
            @Override
            public void login(String username, String password) {
                if (isProxy()) {
                    System.out.println(username + "登录了!");
                } else {
                    System.out.println("请使用指定的代理访问!");
                }
            }
        
            @Override
            public void killBoss() {
                if (isProxy()) {
                    System.out.println(this.name + "在打怪!");
                } else {
                    System.out.println("请使用指定的代理访问!");
                }
            }
        
            @Override
            public void upgrade() {
                if (isProxy()) {
                    System.out.println(this.name + "又升一级!");
                } else {
                    System.out.println("请使用指定的代理访问!");
                }
            }
        
            @Override
            public IGamePlayer getProxy() {
                this.proxy = new GamePlayerProxy(this);
                return this.proxy;
            }
        
            // 检验代理是否被访问
            private boolean isProxy() {
                if (this.proxy == null) {
                    return false;
                } else {
                    return true;
                }
            }
        }
        
      • 场景类Client

        package com.peng.game;
        
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Proxy;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public class Client_IH {
            public static void main(String[] args) throws Exception {
                // 定义一个痴迷的玩家
                IGamePlayer player = new GamePlayer("kungfu~peng");
                // 定义一个Handler
                InvocationHandler handler = new GamePlayIH(player);
                // 获得类的class loader
                ClassLoader cl = player.getClass().getClassLoader();
                // 动态产生一个代理者
                IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl,
                        new Class[] { IGamePlayer.class }, handler);
                // 注意:这里还是用的强制代理---其他代码用强制代理模式
                proxy = proxy.getProxy();
                // 登录
                proxy.login("kungfu~peng", "***");
                // 开始杀怪
                proxy.killBoss();
                // 升级
                proxy.upgrade();
            }
        }
        
      • 执行结果

        kungfu~peng登录了!
        kungfu~peng在打怪!
        kungfu~peng又升一级!
        消费5元!
        
  • 解释:动态代理模式中,既没有创建代理类,也没有实现IGamePlayer接口,这就是动态代理,别急,动态代理可不仅仅这么多内容,还有更重要的,如果想让游戏登录后就发一个信息给我们,防止账号被盗用。怎么办?当然是修正GamePlayIH类啦!!

    • 代码如下

      package com.peng.game;
      
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      
      /**
       * @author kungfu~peng
       * @data 2017年11月17日
       * @description
       */
      public class GamePlayIH implements InvocationHandler {
      
          // 被代理者
          Class cls = null;
          // 被代理的实例
          Object obj = null;
      
          // 我要代理谁
          public GamePlayIH(Object obj) {
              super();
              this.obj = obj;
          }
      
          // 调用被代理的方法
          @Override
          public Object invoke(Object proxy, Method method, Object[] args)
                  throws Throwable {
              Object result = method.invoke(this.obj, args);
              // 如果是登录方法则发送信息
              if (method.getName().equalsIgnoreCase("login")) {
                  System.out.println("有人用我的账号登录!");
              }
              return result;
          }
      }
      
  • 注:这里测试不能用强制代理了,因为获取的代理获取的是getProxy方法 

    package com.peng.game;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    /**
     * @author kungfu~peng
     * @data 2017年11月17日
     * @description
     */
    public class Client_IH {
    	public static void main(String[] args) throws Exception {
    		// 定义一个痴迷的玩家
    		IGamePlayer player = new GamePlayer("kungfu~peng");
    		// 定义一个Handler
    		InvocationHandler handler = new GamePlayIH(player);
    		// 获得类的class loader
    		ClassLoader cl = player.getClass().getClassLoader();
    		// 动态产生一个代理者
    		IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(cl,
    				new Class[] { IGamePlayer.class }, handler);
    		// 这里还是用的强制代理
    		proxy = proxy.getProxy();
    		// 登录
    		proxy.login("kungfu~peng", "***");
    		// 开始杀怪
    		proxy.killBoss();
    		// 升级
    		proxy.upgrade();
    	}
    }
    
    //测试结果
    getProxy
    kungfu~peng登录了!
    kungfu~peng在打怪!
    kungfu~peng又升一级!
    消费5元!
  • 知道结果了,也通过这个意料之外的运行更加清楚动态代理模式,现在你自己手动改下吧!
  • AOP的编程没有使用什么新的技术,但是它对我们的设计、编码也有非常大的影响,对于日志、事务、权限等都可以在系统设计阶段不用考虑,而在设计后通过AOP的方式切过去
  • 动态代理的通用类图
  •  

    • 两条独立发展的线路,动态代理实现代理的职责,业务逻辑Subject实现相关的逻辑功能,两者之间没有必然的耦合功能。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装的任务
    • 代码

      • Subject

        package com.peng.dynamicproxy;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public interface Subject {
            // 业务操作
            public void doSomething(String str);
        }
        
      • RealSubject

        package com.peng.dynamicproxy;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public class RealSubject implements Subject {
        
            @Override
            public void doSomething(String str) {
                // 业务操作
                System.out.println("do Something~~" + str);
            }
        
        }
        
      • MyInvocationHandler

        package com.peng.dynamicproxy;
        
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public class MyInvocationHandler implements InvocationHandler {
            // 被代理的对象
            private Object target = null;
        
            // 通过构造函数传递一个对象
            public MyInvocationHandler(Object _obj) {
                super();
                this.target = _obj;
            }
        
            // 代理方法
            @Override
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                return method.invoke(this.target, args);
            }
        
        }
        
      • DynamicProxy

        package com.peng.dynamicproxy;
        
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Proxy;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public class DynamicProxy<T> {
            public static <T> T newProxyInstance(ClassLoader loader,
                    Class<?>[] interfaces, InvocationHandler h) {
                // 寻找JoinPoint连接点,AOP框架使用元数据定义
                if (true) {
                    // 执行一个前置通知--切入
                    (new BeforeAdvice()).exec();
                }
                return (T) Proxy.newProxyInstance(loader, interfaces, h);
            }
        }
        
      • IAdvice

        package com.peng.dynamicproxy;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public interface IAdvice {
            // 通知方法只有一个
            public void exec();
        }
        
      • BeforeAdvice

        package com.peng.dynamicproxy;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public class BeforeAdvice implements IAdvice {
        
            @Override
            public void exec() {
                System.out.println("执行了前置函数!");
            }
        
        }
        
      • Client

        package com.peng.dynamicproxy;
        
        import java.lang.reflect.InvocationHandler;
        
        /**
         * @author kungfu~peng
         * @data 2017年11月17日
         * @description
         */
        public class Client {
            public static void main(String[] args) {
                // 定义一个主题
                Subject subject = new RealSubject();
                // 定义一个Handler
                InvocationHandler handler = new MyInvocationHandler(subject);
                // 定义主题的代理
                Subject proxy = DynamicProxy.newProxyInstance(subject.getClass()
                        .getClassLoader(), subject.getClass().getInterfaces(), handler);
                //代理行为
                proxy.doSomething("Finish!!");
            }
        }
        
      • 执行效果
        执行了前置函数!
        do Something~~Finish!!
        
    • 动态代理过程

    • 继续扩展DynamicProxy类之SubjectDynamicProxy:

      package com.peng.dynamicproxy;
      
      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Proxy;
      
      /**
       * @author kungfu~peng
       * @data 2017年11月17日
       * @description
       */
      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);
              // 寻找JoinPoint连接点,AOP框架使用元数据定义
              return newProxyInstance(loader, classes, handler);
          }
      
      }
      
    • 测试类更加简洁

      package com.peng.dynamicproxy;
      
      
      /**
       * @author kungfu~peng
       * @data 2017年11月17日
       * @description
       */
      public class Client {
          public static void main(String[] args) {
              // 定义一个主题
              Subject subject = new RealSubject();
              // 定义主题的代理
              Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);
              // 代理行为
              proxy.doSomething("Finish!!");
          }
      }
      

注意

  • 与静态代理区别:加入切面(切入)
  • 实现动态代理的首要条件:被代理类实现一个接口

最佳实践

  • AOP
  • AspectJ
  • 弄清名词:
    • 切面(Aspect)
    • 切入点(JoinPoint)
    • 通知(Advice)
    • 织入(Weave)

声明

  • 摘自秦小波《设计模式之禅》第2版;
  • 仅供学习,严禁商业用途;
  • 代码手写,没有经编译器编译,有个别错误,自行根据上下文改正。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乘风御浪云帆之上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值