zoukankan      html  css  js  c++  java
  • 代理模式

    代理模式的定义 :

    为其他对象提供一种代理以控制对这个对象的访问 . 代理对象起到的是中介的作用 , 可去掉功能服务或添加额外的服务 .

    常见的代理模式简单分为以下几种 :

    • 远程代理
    • 虚拟代理
    • 保护代理
    • 智能引用代理

    远程代理 : 类似于客户端和服务端的关系 , 为不同地理位置的对象提供局域网代表对象 .

    虚拟代理 : 根据需要将资源消耗很大的对象进行延迟 , 真正需要的时候再进行创建 .

          比如 : 当一个网页在加载一个图片时 , 需要比较长的时间 , 这个时候需要等待比较长的时间 , 会影响其他的操作 , 这时我们可以在加载之前用一张虚拟的图片进行填充其位置 , 等到真实的图片加载完毕 , 再进行替换 .

    保护代理 : 可以用来控制权限 .

          比如 : 在一个 BBS 系统中 , 不同类别的用户有不同的权限 , 未登录用户只能浏览帖子 , 登录了的用户能发帖和评论 , 以及删除自己的帖子或者评论 , 但是不能删除别人的贴子 , 同时 , 管理员的权限是可以删除任意帖子 .

    智能引用代理 : 提供对目标对象一些额外的服务 , 同时也会减少一些服务 .

          比如 : 火车票代售点 , 可以代理火车站售票 , 支持电话预定 ( 这个功能火车站没有 ) , 但是不支持退票 .

     

    实现代理的方式有两种 :

    • 静态代理
    • 动态代理

    静态代理 : 代理和被代理对象在代理之前是确定的 , 并且他们都实现了相同的接口或者继承了相同的抽象类 .

    image

    静态代理有种方法 :

    • 继承
    • 聚合

     动态代理 : 代理类是不确定的 .

     

    动态代理也有两种方法 :

    • JDK动态代理
    • CGLIB动态代理

     JDK动态代理实现的步骤 :

    1. 创建一个实现接口 InvocationHandler的类 , 它必须实现 invoke 方法
    2. 创建被代理的类以及接口
    3. 调用 Proxy 类的静态方法 , 创建一个代理类
    4. 通过代理调用方法

    举个栗子 :

           一个汽车类 , 实现了Moveable接口 , 有 move的方法 , 现在需要使用代理实现一个计算运行时间的功能 , 也就是给move方法进行功能的增强 .

      两个基本类 :

          Moveable 接口

    package com.msym.jdkproxy;
    
    public interface Moveable {
        void move();
    }

         Car 类

    package com.msym.jdkproxy;
    
    import java.util.Random;
    
    public class Car implements Moveable{
    
        @Override
        public void move() {
            System.out.println("车子开起来....");
            try {
                Thread.sleep(new Random().nextInt(1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }

     

    创建实现了 InvocationHandle 接口的 TimeHandler

          TimeHandle 类

    package com.msym.jdkproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    /**
     * 
     * @author 码上猿梦
     *  http://www.cnblogs.com/daimajun/
     */
    public class TimeHandler implements InvocationHandler {
    
        private Object target;
        
        public void setTarget(Object target) {
            this.target = target;
        }
        
        /*
         * 参数:
         *     proxy : 代理类的对象
          *     method : 代理对象需要被代理的方法(也就是需要被增强的方法)
         *     args : 被增强方法需要的参数
         * */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("我要开始打日志了,,,,");
            //调用被代理对象的原始方法, 这里的返回值是原始方法的返回值, 如果有返回值则返回, 没有返回值则为null
            Object obj = method.invoke(target);
            System.out.println("日志结束了......");
            //其实这里返回的是null, 因为被增强的方法move本身就没有返回值
            return obj;
        }
    
    }

    测试类 :

    [ 其中的四步就是实现动态代理的四个步骤 ]

    package com.msym.jdkproxy;
    
    import java.lang.reflect.Proxy;
    
    public class Test {
    
        @org.junit.Test
        public void carProxyTest() {
            // 1. 创建处理对象
            TimeHandler h = new TimeHandler();
            // 2. 创建需要被代理的对象(也就是需要被增强的类的对象)
            Moveable m = new Car();
            // 将被代理对象与处理对象绑定
            h.setTarget(m);
            /* 3. 创建代理类对象, 也就是增强后的对象
             * Proxy.newProxyInstance(loader, interfaces, h)
            * 参数解释:
            *     loader : 被代理对象的类加载器
             *     interfaces : 被代理对象实现的接口
             *     h : 实现动态代理的Handle对象 
             * */
            Moveable m1 = (Moveable) Proxy.newProxyInstance(m.getClass().getClassLoader(), m.getClass().getInterfaces(), h);
            // 4. 调用方法, 这时的方法已经是被增强的方法了
            m1.move();
        }
    }

    运行结果 :

    image

    CGLIB动态代理实现步骤 :

    1. 创建一个类实现 MethodInterceptor接口, 实现 intercept方法
    2. 创建第一步中类的对象
    3. 获取代理类实例
    4. 通过代理类实例调用目标方法

    举个栗子 : [ 还是之前的栗子, 给 Car类增强一个打日志的功能 ]

        Car类 : 没有实现任何接口或抽象类

    package com.msym.cglibproxy;
    
    /**
     * 该类不用实现接口或者抽象类
     * @author 码上猿梦
     *  http://www.cnblogs.com/daimajun/
     */
    public class Car {
        public void move(){
            System.out.println("我要开车了.....");
        }
    }

        CglibProxy类 :

    package com.msym.cglibproxy;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    /**
     * 
     * @author 码上猿梦
     *  http://www.cnblogs.com/daimajun/
     */
    public class CglibProxy implements MethodInterceptor {
    
        //创建增强对象, 用于创建子类对象(也就是代理对象)
        private Enhancer enhancer = new Enhancer();
        
        public Object getProxy(Class<?> clazz){
            //设置父类class
            enhancer.setSuperclass(clazz);
            //设置回调对象
            enhancer.setCallback(this);
            //创建代理对象实例(也就是子类对象)
            return enhancer.create();
        }
        
        /**
         * 拦截调用被代理对象的所有方法的方法
         * 参数:
         *     obj : 被代理的类的实例
         *     m : 被代理对象中的方法(通过反射获取)
         *     args : 代理对象方法需要的参数
         *     proxy : 代理类的实例(也就是被代理类子类的实例对象)
         */
        @Override
        public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable {
            //增强的功能:
            System.out.println("日志开始记录了,,,,,");
            /*    原本的功能:
             *  因为是拦截调用父类的方法,所以就不会去调用父类的方法了,这里需要显示的调用父类的方法,
             *     返回值是父类方法的返回值,和JDK代理一样,父类方法如果有返回值,你这里的res就是那个返回值,
             *     如果父类方法没有返回值, 这里的res就是null
             */
            Object res = proxy.invokeSuper(obj, args);
            //增强的功能:
            System.out.println("日志记录结束了......");
            return res;
        }
    
    }

    测试类 :

    package com.msym.cglibproxy;
    /**
     * 
     * @author 码上猿梦
     *  http://www.cnblogs.com/daimajun/
     */
    public class Test {
        
        @org.junit.Test
        public void test(){
            CglibProxy cglib = new CglibProxy();
            //因为动态生成的代理对象是被代理类的子类, 所有可以强转
            Car cat = (Car) cglib.getProxy(Car.class);
            cat.move();
        }
    
    }

    运行结果 :

    image

     

    JDK 代理和 CGLIB 代理的区别 :

    JDK代理 : 只能代理实现了接口的类, 没有实现接口的类不能使用 JDK动态代理

    CGLIB代理 : 针对类来进行代理, 对指定目标类产生一个子类 , 通过方法拦截技术拦截所有对父类方法的调用, 在拦截方法中(也就是 intercept()方法中再次调用父类的方法, 同时在调用的前后添加若干功能代码 )

     

     

     

     

     

     

     

     

     

     

    1

  • 相关阅读:
    简单理解OOP——面向对象编程
    SpringMVC拦截器
    Vue简洁及基本用法
    springMVC实现文件上传下载
    Python笔记⑤爬虫
    Python笔记4
    Python笔记3
    Python基础语法笔记2
    Python基础入门语法1
    Navicat连接mysql时候出现1251错误代码
  • 原文地址:https://www.cnblogs.com/daimajun/p/7294360.html
Copyright © 2011-2022 走看看