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

    设计模式-----代理模式

        一、定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

        说明:例如我想租房子,虽然我可以去统计有哪些房子正在出租,每个房子是什么样的户型,房子的价格等。。。,但我就想租个房子,只想选择租或不租,不想去了解房源信息等一系列事,于是就有了租房中介,他会给你收集好这些信息,你只用选择租或不租就行了。

        代理模式包含如下角色:

            ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。

            RealSubject:真实主题角色,是实现抽象主题接口的类。

            Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

        二、静态代理和动态代理

    我们有多种不同的方式来实现代理。如果按照代理创建的时期来进行分类的话, 可以分为两种:静态代理、动态代理。静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。动态代理是在程序运行时通过反射机制动态创建的。

    1)静态代理

    /**
     * 抽象主题角色
     * @author rdb
     *
     */
    public interface Sleep {
        void sleep();
    }
     
    /**
     * 真实主题角色
     * @author rdb
     *
     */
    public class SleepImpl implements Sleep {
        @Override
        public void sleep() {
            System.out.println("熟睡中");
        }
    }
     
    /**
     * 代理角色
     * @author rdb
     *
     */
    public class SleepProxy implements Sleep {
        private Sleep sleep;
        public SleepProxy(Sleep sleep) {
            this.sleep = sleep;
        }  
        @Override
        public void sleep() {   
            System.out.println("睡觉前要刷牙");
            sleep.sleep();
            System.out.println("睡醒后要吃早饭");
        }
    }
     
    /**
     * 测试代码
     * @author rdb
     *
     */
    public class ProxyTest {
         
        public static void main(String[] args) {
            Sleep sleep = new SleepImpl();
            Sleep sleepProxy = new SleepProxy(sleep);
            sleepProxy.sleep();
        }
    }
     
    结果:睡觉前要刷牙
          熟睡中
          睡醒后要吃早饭

    2)动态代理(JDK)

    静态代理有一个明显的缺点:一个主题类和一个代理类一一对应,所以我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。  在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态的来创建。   

    /**
     * 抽象主题角色
     * @author rdb
     *
     */
    public interface Sleep {
        void sleep();
    }
     
    /**
     * 真实主题角色
     * @author rdb
     *
     */
    public class SleepImpl implements Sleep {
        @Override
        public void sleep() {
            System.out.println("熟睡中");
        }
    }
     
    /**
     * 动态处理器
     * @author rdb
     *
     */
    public class DynamicProxyHandler implements InvocationHandler {   
        private Object obj ;
        public  DynamicProxyHandler(final Object obj) {       
            this.obj = obj;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("睡觉前要刷牙");
            Object result = method.invoke(obj, args);
            System.out.println("睡醒后要吃早饭");
            return null;
        }
    }
    /**
     * 测试代码
     * @author rdb
     *
     */
    public class ProxyTest {
        public static void main(String[] args) {              
            Sleep sleep = new SleepImpl();
            DynamicProxyHandler dph = new DynamicProxyHandler(sleep);
            Sleep sleepProxy = (Sleep) Proxy.newProxyInstance(sleep.getClass().getClassLoader(),
                    sleep.getClass().getInterfaces(), dph);
            sleepProxy.sleep();
        }
    }
     
    结果:睡觉前要刷牙
          熟睡中
          睡醒后要吃早饭

    注意:Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,动态处理器   

    JDK动态代理的一般步骤:(1)创建动态处理器实现InvocationHandler接口,实现invoke()方法

                                            (2)创建被代理的类及接口

                                            (3)调用Proxy的静态方法,创建代理类

                                            (4)通过代理类调用方法

    JDK生成的代理类本身就继承了Proxy类,Java只允许单继承,所以JDK的动态代理不能完成继承式动态代理(只能完成接口的动态代理,不能实现类的动态代理),但是我们可以用CGLIB的方式实现继承式的动态代理

    3)动态代理(CGLIB)

    //具体主题
    public class Sleep1 {
        public void sleep() {
            System.out.println("熟睡中");
        }
    }
     
    /**
     * 
     * @author rdb
     *
     */
    public class CGLibProxy implements MethodInterceptor {
     
        private Object obj;
         
        //创建代理类
        public Object getInstance(Object obj) {
            this.obj = obj;//主题对象
            Enhancer enhancer = new Enhancer();//创建加强器,用来创建动态代理类
            enhancer.setSuperclass(this.obj.getClass());
            //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
            enhancer.setCallback(this);
            //返回代理类
            return enhancer.create();
        }
         
        //回调方法实现
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy)
         throws Throwable {
            System.out.println("睡觉前要刷牙");
            Object result = methodProxy.invokeSuper(obj, args);
            System.out.println("睡醒后要吃早饭");
            return result;
        }
     
    }
     
    /**
     * 测试代码
     * @author rdb
     *
     */
    public class ProxyTest {
         
        public static void main(String[] args) {
            Sleep1 sleep = new Sleep1();
            CGLibProxy proxy = new CGLibProxy();
            Sleep1 s = (Sleep1) proxy.getInstance(sleep);
            s.sleep();
        }
    }
     
    结果:睡觉前要刷牙
          熟睡中
          睡醒后要吃早饭

    总结:静态代理虽然能完成代理功能,但我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。

               JDK动态代理很美很强大,但由于java的单继承,只能实现接口的动态代理,不能实现类的动态代理

                CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

  • 相关阅读:
    怎么用一句话把一个集合的属性值根据条件改了,而其他值不变
    【算法导论学习笔记】第3章:函数的增长
    【算法导论学习笔记】第2章:算法基础
    【算法导论学习笔记】第1章:算法在计算中的作用
    【Python爬虫学习笔记(3)】Beautiful Soup库相关知识点总结
    【Python爬虫学习笔记(2)】正则表达式(re模块)相关知识点总结
    【Python爬虫学习笔记(1)】urllib2库相关知识点总结
    string类型转换JSONObjec类型,并取出对应的值
    mybatis的Example的使用
    github上传项目
  • 原文地址:https://www.cnblogs.com/jnba/p/10677763.html
Copyright © 2011-2022 走看看