zoukankan      html  css  js  c++  java
  • Java 代理[1]-静态代理、JDK动态代理和Cglib动态代理的简单使用

      前段时间看了下 Mybatis , 顺便梳理下关于代理这一块的知识。 

      这里自己简单想了一个近期关于火车票的实例,可能很多地方还有不当之处,还请谅解。

    //卖火车票
    public interface TrainTicketSeller {
        void query();
        void sell();
    }
    
    //12306
    public class Seller12306 implements TrainTicketSeller {
        
        //实际业务
        @Override
        public void query() {
            System.out.println("您好,您回家的票还有……0张");
        }
    ​
        //实际业务
        @Override
        public void sell() {
            System.out.println("您已购买回家的票!");
        }
    ​
    }

      下面分别用3种代理来实现同样的功能。

    一、静态代理

      

      第三方代理售票,比如美团、铁友啊

    //第三方代理售票,加了漂亮的头和尾。。。>..
    public class MeiTuanSeller implements TrainTicketSeller {
        private Seller12306 seller12306;
    ​
        public MeiTuanSeller(Seller12306 seller12306) {
            this.seller12306 = seller12306;
        }
    ​
        @Override
        public void query() {
            //额外功能操作
            System.out.println("*********************");
            //实际业务
            seller12306.query();
            //额外功能操作
            System.out.println("********************");
        }
    ​
        @Override
        public void sell() {
            System.out.println("*********************");
            seller12306.sell();
            System.out.println("********************");
        }
    }

      使用

    //调用
    TrainTicketSeller seller = new MeiTuanSeller(new Seller12306());
    seller.query();
    seller.sell();
    ​
    /**输出结果:
    **********************
    您好,您回家的票还有……1张
    **********************
    **********************
    您已购买回家的票!
    **********************
    **/

    问题1,不用代理可以么?

    ​   当然是可以的,就想买个票,直接上12306就行,不嫌弃美丑难用。

    问题2,代理中新加的功能,不能直接修改被代理方或者在被代理方增加么?

    ​   当然也是可以的,但是。。为什么12306不改呢?就光卖个票都被喷成啥样了,还有心思至于网站好不好看交互怎么样?几亿人用了这么多年这个样式,说改就改貌似不太合适吧? 好,就算你改了。改完,立马被喷了大家都用不惯,那是回到原来的样式啊,还是再换个新的呢?不管怎么样还是得再改一次。(修改有成本和风险,甚至可能影响核心系统稳定)

    ​   但是如果第三方售票来做这个事情,第一12306不用动,第二如果我觉得第三方也不好看不好用,我还是能继续用原来的12306,第三如果美团做的不好看,旁边360、铁友貌似还挺好看的,我去试试。

      个人总结起代理的优点,本质上还是符合单一职责原则和开闭原则(对扩展开放,对修改封闭)。核心职责和非核心职责的的划分清晰,保持原系统的稳定性的同时增加了扩展性

    ​   可能会存在的缺点呢?如果被代理类功能繁杂多样,代理类只会更复杂臃肿。另一个问题是,代理类的新功能代码比较难复用。这里就可以引出动态代理了。

    二、动态代理

      动态代理好处?本质上还是代码复用的问题。代理功能只用写一遍啊。Java 动态代理一般有两种,一种是通过接口来实现(Jdk动态代理),一种是通过继承来实现(cglib)。

    1、JDK 动态代理

    ​   用 JDK 动态代理实现上面一样的功能。

    //代理类不跟任何具体被代理有强绑定关系,理论上所有需要 doAfter() 和 doAfter()的都可以使用此代理
    public class MtDyProxy implements InvocationHandler {
        /**
         * 被代理对象
         */
        private Object target;
    ​
        public Object bind(Object target) {
            this.target = target;
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(), this);
        }
    ​
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            doBefore();
            Object invokeRes = method.invoke(target, args);
            doAfter();
            return invokeRes;
        }
    ​
        private void doAfter() {
            System.out.println("**********************");
        }
    ​
        private void doBefore() {
            System.out.println("*********************");
        }
    ​
    }

      使用

    TrainTicketSeller seller = (TrainTicketSeller) new MtDyProxy().bind(new Seller12306());
    seller.query();
    seller.sell();
    ​
    /**输出结果:
    **********************
    您好,您回家的票还有……1张
    **********************
    **********************
    您已购买回家的票!
    **********************
    **/

      假如现在另一个地方也需要此代理提供的功能,我的代理不需要做任何改动,只需要在调用的时候,更改bind()参数和强转类型即可。

    ISubject subject = (ISubject) new MtDyProxy().bind(new ReadSubject());
    subject.do();

      由于代理类是动态生成的,所以调用链不够清晰,不能一眼看出一步步是怎么走的。其实这点,可以将动态代理的生成的Class输出到本地,再反编译出来就OK了。这个点下次来说明。

      动态代理最大的缺点是,必须依赖接口。生成的代理类中只会有实现接口的方法。如果需要被代理的方法,没有实现接口,那很遗憾就无法使用 Jdk动态代理。这个时候,就需要 Cglib 代理了。

    2、CGLIB 动态代理

      通过 CGLIB 来实现代理

    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    ​
    public class CglibCommonProxy implements MethodInterceptor {
    ​
        /**
         * 被代理对象,供代理方法中进行真正的业务方法调用
         */
        private Object target;
    ​
        //相当于JDK动态代理中的绑定
        @SuppressWarnings("unchecked")
        public Object getInstance(Object target) {
            this.target = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.target.getClass());
            //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
            enhancer.setCallback(this);
            // 创建动态代理类对象并返回
            return enhancer.create();
        }
    ​
        // 实现回调方法
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            doBefore();
            //调用业务类(父类中)的方法
            Object result = proxy.invokeSuper(obj, args);
            doAfter();
            return result;
        }
    ​
        private void doAfter() {
            System.out.println("**********************");
        }
    ​
        private void doBefore() {
            System.out.println("**********************");
        }
    ​
    }

      使用

    @Test
    public void cglibProxy() throws Exception {
        CglibCommonProxy cglib = new CglibCommonProxy();
        //注意这里,不需要依赖接口
        Seller12306 seller = (Seller12306) cglib.getInstance(new Seller12306());
        seller.query();
        seller.sell();
    }
    ​
    /**输出结果:
    **********************
    您好,您回家的票还有……1张
    **********************
    **********************
    您已购买回家的票!
    **********************
    **/

      这里只是简单了做了下使用说明,比较深的东西希望自己能在下次给出来。

    三、总结

      现在已经很少看到单独使用静态代理的了。。。

      前面也有提到,JDK动态动态代理是通过接口来实现的,而 Cglib 是通过继承来实现的。这也是 Cglib 相对于前者的一个优势。

    四、补充

    • 代理模式和装饰器模式

      今天也看了不少有讨论这这两者区别异同的文章,一些统一的说法就是装饰为了增强,代理更偏控制。

      说说自己的看法,两者确实十分相似,但是其实没有必要说把两个东西完全区分开,本质上都是为了解耦易扩展。装饰也好代理也好,只是文字上的不同而已,不必要纠结许多年前的理论文字了。

      实际应用中,我相信没人说会在用的时候去纠结要用装饰还是代理。我们应该在意的点是,怎么更好的解决问题。设计模式只是一些经典的思想,思想应当是一直在进步的。况且现在 80% 的应用粗制滥造的程度,还达不到纠结设计模式的时候。

    嘿咻
  • 相关阅读:
    Charles手机端抓包--证书
    新浪微盘批量下载
    [3140] Sublime Text 2.x, 3.x 通用注册码、密钥集合
    栈的数组实现
    栈的链表实现
    基数排序
    多项式相乘-数组实现
    最大子序列和问题的四种算法
    链表的实现
    时间同步算法与Simple Ring-based election algorithm算法分析-转载
  • 原文地址:https://www.cnblogs.com/wdado/p/java_proxy1.html
Copyright © 2011-2022 走看看