zoukankan      html  css  js  c++  java
  • JAVA设计模式 5【结构型】代理模式的理解与使用

    今天要开始我们结构型 设计模式的学习,设计模式源于生活,还是希望能通过生活中的一些小栗子去理解学习它,而不是为了学习而学习这些东西。

    结构型设计模式

    结构型设计模式又分为

    • 类 结构型
    • 对象 结构型

    前者使用对象的继承机制来组织对象和类
    后者采用组合聚合 的方式来组合对象。

    代理模式 Proxy

    理解代理一词 代理表达的是:为某个对象提供一种代理,用于控制该对象的访问,让客户端间接的访问该对象,从而限制、或者增强源对象的一些特性。

    举个栗子

    image.png

    从国内科学SW,访问谷歌查阅一些资料的时候,我们肯定务必会借助一些代理器 也就是通常所说的VPN,代理的服务器可以帮助我们完成这些操作。

    静态代理

    画个图理解一下

    image.png

    需要说明的地方有:

    • 抽象父类或者接口:定义了这个代理可以代理的方法。比如定义了一个SearchSubject 实现它的子类必须要实现对应的search() 方法。
    /**
     * 抽象主题,可以进行搜索
     */
    public abstract class SearchSubject {
        /**
         * 可以进行搜索的操作
         */
        public abstract void search();
    }
    
    • 真实对象:真实对象也就是具体将要被代理的方法,这个真实对象的方法我们要通过代理类间接的去访问

    众所周知,国内访问不到Google,需要代理才行。

    public class Google extends SearchSubject {
        @Override
        public void search() {
            System.out.println("Google 搜索引擎");
        }
    }
    
    • 代理类:也就是VPN ,帮助我们访问真实对象 的某些方法,并且还可以做一些增强。比如在访问真实对象之前做一些事情,之后做一些事情。
    /**
     * VPN 代理
     * 静态代理也需要实现抽象主题
     */
    public class VPNProxy extends SearchSubject {
        /**
         * 含有真实主题
         */
        private Google google;
    
        @Override
        public void search() {
            if (null == google) {
                google = new Google();
            }
            this.before();
            /**
             * 调用真实对象的方法
             */
            google.search();
            this.after();
        }
        /**
         * 增强方法
         */
        public void before() {
            System.out.println("VPN 开始执行。。。");
        }
        public void after() {
            System.out.println("VPN 结束执行");
        }
    }
    

    执行调用代理

    VPNProxy proxy = new VPNProxy();
    proxy.search();
    ------------------
    VPN 开始执行。。。
    Google 搜索引擎
    VPN 结束执行
    

    以上就是我们要学习的第一种代理方式:静态代理

    动态代理

    假设我们还需要代理一个对象呢?比如必应 假设必应搜索我们国内访问不到,必须使用代理的话,是不是又得重新创建两个对象

    • 真实对象必应搜索
    • 代理对象必应搜索的代理

    这就不利于我们系统的扩展性,假设有很多需要代理的,那岂不是写一大堆。

    因此,动态代理由此而生。

    这里我们使用JDK 提供的动态代理

    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h){}
    
    • ClassLoader 类加载器
    • interfaces 加载的接口
    • InvocationHandler 增强处理器以及调用代理的类

    创建一个可供实现的搜索接口

    /**
     * 搜索接口
     */
    public interface SearchInterface {
        String search();
    }
    

    谷歌搜索引擎实现了这个接口,并且将名称作为返回值返回。

    public class GoogleSearch implements SearchInterface {
        @Override
        public String search() {
            System.out.println("Google 搜索引擎");
            return "Google";
        }
    }
    

    创建一个搜索增强器,并且创建了两个方法的增强,在调用代理之前和之后,都加入了一些方法。

    /**
     * 搜索处理器
     */
    public class SearchHandler implements InvocationHandler {
    
        private void before() {
            System.out.println("handler start");
        }
    
        private void after() {
            System.out.println("handler stop");
        }
    
        private SearchInterface obj;
    
        public SearchHandler(SearchInterface obj) {
            this.obj = obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            this.before();
            /**
             * 执行代理方法
             */
            Object result = method.invoke(obj, args);
            System.out.println("result=" + result.toString());
            this.after();
    
            return result;
        }
    }
    

    创建一个动态代理工厂,将我们需要代理的接口传入,并且传入接口的处理类,即可实现接口的增强处理。

    /**
     * 动态代理工厂
     */
    public class ProxyFactory {
        /**
         * 目标对象
         */
        private SearchInterface object;
        private InvocationHandler handler;
    
        public ProxyFactory(SearchInterface obj, InvocationHandler handler) {
            this.object = obj;
            this.handler = handler;
        }
        /**
         * 获取代理对象
         * @return
         */
        public Object getProxyObj() {
            ClassLoader classLoader = object.getClass().getClassLoader();
            Class<?>[] interfaces = object.getClass().getInterfaces();
            return Proxy.newProxyInstance(classLoader, interfaces, handler);
        }
    }
    

    创建一个具体的接口对象,传入我们的代理工厂,并且将其处理器也同时传入,我们就可以得到一个代理对象。

            SearchInterface search = new GoogleSearch();
    
            System.out.println("1id=" + search);
            InvocationHandler handler = new SearchHandler(search);
    
            ProxyFactory factory = new ProxyFactory(search, handler);
            SearchInterface google = (SearchInterface) factory.getProxyObj();
    
            System.out.println("2id=" + google);
            google.search();
    -----------------
    1id=impl.GoogleSearch@1b6d3586
    
    handler start
    result=impl.GoogleSearch@1b6d3586
    handler stop
    
    2id=impl.GoogleSearch@1b6d3586
    handler start
    Google 搜索引擎
    result=Google
    handler stop
    

    从上面的代码我们发现:

    • 代理的对象与我们创建的对象有所不同
    • 在生成代理对象的时候、已经执行了一遍invoke() 方法
    • 通过代理对象调用具体方法的时候也执行了一遍invoke()

    老衲画个图

    image.png

    这样就好理解多了,代理工厂需要一个代理类、以及这个代理类的增强方法(处理器),通过代理工厂生成的代理对象,实现对对象的增强处理。

    动态代理的总结

    • 代理类不需要实现接口,但是具体对象还是需要实现接口。

    Cglib代理

    上面两种代理,都是需要代理类、或者是具体的目标对象实现某个接口的基础上出现的,假设没有这个接口的显示,我只想在某个具体的对象上加入增强的话,如何实现呢?

    Cglib代理又被称作子类代理,就是代理一个具体的子类

    因为Spring 已经引入了相关的Cglib 的依赖,我们直接在Spring 的环境下进行测试。

    创建一个具体的子类。没有实现任何的接口

    public class BingSearch {
        public void search() {
            System.out.println("必应搜索。。。");
        }
    }
    

    创建类实现一个方法拦截器,其实名字就是这样叫的。我们的代理对象,是通过工具类拿出来的。

    public class ProxyFactory implements MethodInterceptor {
    
        //维护目标对象
        private Object target;
    
        public ProxyFactory(Object target) {
            this.target = target;
        }
        private void before() {
            System.out.println("代理类前置处理。。");
        }
        private void after() {
            System.out.println("代理类后置处理。。");
        }
        public Object getProxy() {
            //1.工具类
            Enhancer en = new Enhancer();
            //2.设置父类
            en.setSuperclass(target.getClass());
            //3.设置回调函数
            en.setCallback(this);
            //4.创建子类(代理对象)
            return en.create();
        }
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
            /**
             * 执行方法
             */
            this.before();
            Object result = method.invoke(target, objects);
            this.after();
            return result;
        }
    }
    

    在main 方法对一个具体的类进行增强代理。

        public static void main(String[] args) {
    
            ProxyFactory proxyFactory = new ProxyFactory(new BingSearch());
    
            BingSearch bing = (BingSearch) proxyFactory.getProxy();
            bing.search();
        }
    ---------
    代理类前置处理。。
    必应搜索。。。
    代理类后置处理。。
    

    小结

    本节,将我们最常用的两种代理模式进行了一些讲解,其实最重要的是JDK动态代理Cglib 具体方法代理增强。因为大家已经拥抱Spring 的怀抱了,这两种代理还是很重要的,Spring的AOP 切面也是一种基于动态代理的方式实现。非常好用,在Spring 声明式事务当中,一个注解即可搞定许多冗余的编程式事务,这无不归功于 强大的动态代理

    鸣谢&参考

    https://www.cnblogs.com/leeego-123/p/10995975.html

    代码

    https://gitee.com/mrc1999/Dev-Examples

    欢迎关注

    banner

  • 相关阅读:
    感动于细节,记我的一个同学(君君)
    求职时,我问过的问题
    毕业留言,写给我的一个同学
    偶是一个如此善良的人
    温柔地对待仇人,就像对待情人一样!
    小改机箱,支持硬盘调头,让磁盘对拷更方便
    心中的女朋友:(标准)
    准备开始看以下书籍
    成都文化公园游后感
    ”中国的教育与计算机“读后感
  • 原文地址:https://www.cnblogs.com/ChromeT/p/13415017.html
Copyright © 2011-2022 走看看