zoukankan      html  css  js  c++  java
  • 理解Java动态代理(1)—找我还钱?我出钱要你的命

    代理模式是最常用的一个设计模式之一,理解起来也是很简单,一张图足以说明了,LZ就不废话了。

    至于代理模式能干嘛也不是LZ今天想说的,今天主要想简单介绍下JAVA里面的动态代理。“动”当然是相对“静”来说的,那么什么是静,怎么就又动了呢?LZ想举个生活中常见的例子来说明,俗话说“谈钱伤感情”,但生活所迫LZ曾经可没少找人借个一百两百五的,话说借钱一时爽,还钱……(请自行造句),好点的心平气和的委婉的说,横点的就拳脚相加啊。我们来用接口表示下借钱者这个角色,他们可以采取peace或force的方式找我还钱:

    /**
     * 借钱方
     * 
     * @author moon
     * 
     */
    public interface ILender {
        /**和平方式*/
        public long peace(String name);
    
        /**暴力方式*/
        public long force(String name);
    }

    我月中找张三借钱:

    public class ZhangSanImpl implements ILender {
    
        public long peace(String name) {
            System.out.println("心平气和的找" + name + "要账。");
            return 100;
        }
    
        public long force(String name) {
            System.out.println("以暴力方式找" + name + "要账");
            return 250;
        }
    
    }

    月底又找李四借钱(都是好欺负的货,动手干不过我):

    public class LiSiImpl implements ILender {  
          
        public long peace(String name) {
            System.out.println("心平气和的找" + name + "要账。");
            return 100;
        }
    
        public long force(String name) {
            System.out.println("以暴力方式找" + name + "要账");
            return 250;
        }
    } 

    张三和李四发现我老是借钱又拖帐,于是就合伙找了年级里一个比较横的角色(外号"JDK",由来是因为我叫jd,他是jd killer)来代他们要帐,好说不行就动手。看这就是代理模式。

    /**
     * 借钱者的代理人
     * 
     * @author jdzhan
     * 
     */
    public class CommonLenderProxy implements ILender {
    
        /** 委托者 */
        private ILender delegator;
    
        public CommonLenderProxy(ILender delegator) {
            this.delegator = delegator;
        }
    
        public long peace(String name) {
            return delegator.peace(name);
        }
    
        public long force(String name) {
            System.out.println("开始工作");
            // 执行方法
            long result = delegator.force(name);
            System.out.println("搞定收工");
            return result;
        }
    
    }

    于是乎我屈于淫威或者武力只能乖乖还钱鸟:

     // Common Proxy
     CommonLenderProxy proxy = new CommonLenderProxy(new ZhangSanImpl());
     long money = proxy.force("zhanjindong");
     System.out.println("要回了" + money + "块QB");
    开始工作
    以暴力方式找zhanjindong要账
    搞定收工
    要回了250块QB

     Ok,上面我用了亲身的一个小栗子说明了静态代理,那动态代理到底怎么实现,又有什么好处呢?我们先直接把上面的实现给出来。JDK动态代理中包含一个接口和一个类: 

    InvocationHandler接口:

    public interface InvocationHandler { 
      public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 
    }

    Proxy类:

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
    InvocationHandler h) 
                                   throws IllegalArgumentException 

    "JDK"觉得代人要钱很有钱途,于是搞了个“帮会”:

    /**
     * “JDK”代人人要账帮会
     * 
     * @author jdzhan
     * 
     */
    public class DynamicLenderProxy implements InvocationHandler {
        private Object target;
    
        /**
         * 绑定委托对象并返回一个代理类
         * 
         * @param target
         * @return
         */
        public Object bind(Object target) {
            this.target = target;
            // 取得代理对象
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); 
        }
    
        /**
         * 调用方法
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            if (method.getName().equals("force")) {
                Object result = null;
                System.out.println("开始工作");
                // 执行方法
                result = method.invoke(target, args);
                System.out.println("工作结束");
                return result;
            } else {
                return method.invoke(target, args);
            }
    
        }
    
    }

    是不是感觉比之前牛X多了,当然目的还是找人要账,可能是我也可能是马六:

     //JDK Dynamic Proxy
     DynamicLenderProxy proxy = new DynamicLenderProxy();
     ILender bookProxy = (ILender) proxy.bind(new LiSiImpl());
     long money = bookProxy.force("maliu");
     System.out.println("要回了" + money + "块QB");
    心平气和的找maliu要账。
    要回了100块QB

    我们通常还可以把动态代理跟注解结合起来用,“帮会”越来越大,想立稳得提供多种业务,比如暴力要账,可以让人选择用什么样的暴力,是赤手空拳还是刀墙棍棒伺候:

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Force {
        
        String weapon() default "拳头";
    
    }

    有了这个注解可以在接口里使用:

    /**
     * 借钱方
     * 
     * @author moon
     * 
     */
    public interface ILender {
        /**和平方式*/
        public long peace(String name);
    
        /**暴力方式*/
        @Force(weapon = "AK47")
        public long force(String name);
    }

    改下DynamicLenderProxy里的invoke方法:

    /**
         * 调用方法
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            if (method.isAnnotationPresent(Force.class)) {
                Force force = method.getAnnotation(Force.class);
                String weapon = force.weapon();
                System.out.println("用" + weapon + "开始工作");
                Object result = method.invoke(target, args);
                System.out.println("搞定收工");
                return result;
    
            } else {
                return method.invoke(target, args);
            }
        }

    拿AK47找人要钱谁敢不还:

    用AK47开始工作
    以暴力方式找zhanjindong要账
    搞定收工
    要回了250块QB

    (这里有个问题注解只能用在接口里,用在实现类里如何获取到呢?)

    JDK动态代理的使用就是这么简单,下面就说为什么叫动态代理了。假设有一天我为实在还不了钱了,于是就想花钱找人把借钱给我的人干掉,于是我成了个凶手:

    public interface IMurderer {
    
        @Force(weapon = "沙漠之音")
        public long kill(String name);
    }
    public class JdzhanImpl implements IMurderer {
    
        public void kill(String name) {
            System.out.println("找我还钱?我要你" + name + "的命");
        }
    
    }

    恰好这时"JDK"拓展了业务,也干杀手这一行当了,于是乎当初找我还钱的人成了我雇的杀手,但是实现一点没动:

      DynamicLenderProxy2 proxy = new DynamicLenderProxy2();
      IMurderer murderer = (IMurderer) proxy.bind(new JdzhanImpl());
      murderer.kill("LiSi");
    用沙漠之音开始工作
    找我还钱?我要你LiSi的命
    搞定收工

    好了,这个我胡扯的例子说完了,动态代理的好处也就体现出来了:

    可以为不同的接口进行代理

    如果我们用静态代理的话那么每个接口我们都得写一个代理类。

    但是竟然动态为什么不更灵活点呢,JDK的动态代理还是需依赖接口的,接口就像一个契约或收据,你找我还钱最起码得有欠条吧,下篇文章介绍下Cglib动态代理,就是怎么在没有收据的情况下找人还钱。

  • 相关阅读:
    2020软件工程个人作业06——软件工程实践总结作业
    2020软件工程作业05
    2020软件工程作业00——问题清单
    2020软件工程作业03
    2020软件工程作业02
    2020软件工程作业01
    Ubuntu中安装最新 Node.js 和 npm
    英语学习单词篇一
    Golang之内存读写
    Golang之正则表达式的使用
  • 原文地址:https://www.cnblogs.com/zhanjindong/p/3730805.html
Copyright © 2011-2022 走看看