• 代理模式(静态、动态)


          代理模式的使用价值还是挺高的,各种框架中都用到了。把基础认真看看,学框架的时候也容易了。

    关于静态代理:

        代理模式的应用场景:

    如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:

        1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。

        2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。

        使用代理模式,可以将功能划分的更加清晰,有助于后期维护!

    代理模式的要点:

        1、“增加一层间接层”是软件系统中对许多负责问题的一种常见解决方法。在面向对象系统中,直接使用某些对象会带来很多问题,作为间接层的proxy对象便是解决这一问题的常用手段。

        2、具体proxy设计模式的实现方法、实现粒度都相差很大,有些可能对单个对象作细粒度的控制,有些可能对组件模块提供抽象代理层,在架构层次对对象作proxy。

        3、proxy并不一定要求保持接口的一致性,只要能够实现间接控制,有时候损及一些透明性是可以接受的。

                                image

    (这个来源于网络)

    下面这个是源代码:

    //抽象主题类:
    public interface subject {
        //抽象主题类
        public void doMethod();
    
    }
    
    //具体的实现类:
    public class realSubject implements subject {
    
        @Override
        public void doMethod() {
            // TODO Auto-generated method stub
            System.out.println("excute the subject method");
        }
    
    }
    
    //代理类
    package com.designpatten.staticproxy;
    
    public class realProxy implements subject {
    
        //实现对于subject类的代理
        subject sub;
        //构造函数 注意生成一个代理类的时候 要现将将原有的类的实例传入
        public realProxy(subject sub){
            this.sub=sub;
        }
        
        @Override
        public void doMethod() {
            // TODO Auto-generated method stub
            //预处理
            preprocess();
            //执行主题的方法
            sub.doMethod();
            //后处理
            postprocess();
        }
        
        public void preprocess(){
            System.out.println("before excusion of the domethed");
        }
        
        public void postprocess(){
            System.out.println("after excusion of the domethed");
        }
    
    }
    
    //测试类:
    import java.net.Proxy;
    
    public class Test {
        
        public static void main(String[]args){
            
            //利用多态
            subject sub=new realSubject();
            subject proxy=new realProxy((realSubject)sub);
            proxy.doMethod();
            
        }
        
    }
    
    //运行结果:
    /*
    before excusion of the domethed
    excute the subject method
    after excusion of the domethed
    */

    这样就在原有的基础上,实现了对于方法的扩展。

        感觉代理模式的比较核心的地方有两个:

        1 代理类和真实的主题类,实现了同一个接口,这样的话,所有对于真实类的主题中的方法的调用全可以通过代理类来进行,由于方法名是一样的,感觉比较方便。

        2 这样的话就是符合了对扩展开放,对修改封闭的原则,比如想要在原来的类处理的基础上再加上一个预处理过程,一个后处理过程,这样可以通过代理来进行,不用对原来的类的核心方法进行修改,因此在代理类的实现中要加上一个对于实际主题类的引用作为属性。(纯粹的代理并没有任何意义 对原来的方法的功能的增强才是代理模式的核心)

    clip_image001[11]

    关于动态代理:


        比如有多个类,在每个类的某个方法的前面和后面都要增加预处理与后处理的方法,如果按照上面静态代理的方式来,得为每个类都再写一个代理类比较麻烦,这个就用到了动态代理。

        再往深入一点,java本身对代理模式提供了支持,说明代理模式的用途还是很广泛的java主要是通过Proxy类和InvocationHandler接口来给实现对代理模式的支持动态代理,而动态代理是许多框架的基础 Spring 的AOP技术就是基于动态代理来实现的。

    经典论述:

        Proxy类的设计用到代理模式的设计思想,Proxy类对象实现了代理目标的所有接口,并代替目标对象进行实际的操作。但这种替代不是一种简单的替代,这样没有任何意义,代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截。所以,Proxy应该包括一个方法拦截器,来指示当拦截到方法调用时作何种处理。InvocationHandler就是拦截器的接口。

       还是先把代码放进来:

    //首先是一个接口即是抽象主题类:
    
    public interface subject {
        //抽象主题类
        public void doMethod(String str);
    
    }
    
    //之后是两个对于这个抽象的具体的实现,即是被代理类:
    package com.designpatten.dynamicproxy;
    
    public class realSubject1 implements subject {
    
        @Override
        public void doMethod(String str) {
            // TODO Auto-generated method stub
            System.out.println("excute the subject in method1:"+str);
        }
    
    }
    
    package com.designpatten.dynamicproxy;
    
    public class realSubject2 implements subject{
    
        @Override
        public void doMethod(String str) {
            // TODO Auto-generated method stub
            System.out.println("excute the subject in method2:"+str);
        }
        
    
    }
    
    //之后是一个继承了InvocationHandler的类:
    
    package com.designpatten.dynamicproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class dynamicSubject implements InvocationHandler{
    
        //由于是动态代理 不知道传递进来的是什么类 这里用object
        private Object subject;
        public dynamicSubject(Object obj){
            this.subject=obj;
        }
        
        //object proxy  Method method Object[]obj
        //这里的arg0是在运行的时候 实际生成的$Proxy类的实例
        //这里对invoke方法进行重写 把新加进来的方法补充进去
        @Override
        public Object invoke(Object arg0, Method arg1, Object[] arg2)
                throws Throwable {
         //由于不知道方法是不是有返回值 默认返回null要是有返回值的话 也可以再添加
         //反正在方法的定义中 返回值是object
            preprocess();
           //反射 执行 method arg1 方法 这个方法 是实例类arg0的  方法的参数是arg2
           //注意这里传参数的技巧是很关键的
            //错了很多次了 执行的是subject类(传进来的被代理的类)的arg1(Method类)类方法  
            //第二个参数是arg2   通过反射来进行
            //注意这里的arg1是一个Method类的实例
    //只有这一句 调用的是 真实主题的方法
    arg1.invoke(subject,arg2); postprocess(); //把三个参数输出来下 System.out.println("Object arg0:"+arg0.getClass()+" Method arg1:"+arg1.getName()+" Object[] arg2:"+arg2[0].getClass()); return null; } public void preprocess(){ System.out.println("before excusion of the domethed"); } public void postprocess(){ System.out.println("after excusion of the domethed"); } } //实际进行测试的类: package com.designpatten.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Test { public static void main(String[]args){ //生成两个被代理类的实例 realSubject1 realsubject1=new realSubject1(); realSubject2 realsubject2=new realSubject2(); //生成两个InvocationHandler的实例 这里可以理解为 对真实主题类设置拦截器 InvocationHandler handler1=new dynamicSubject(realsubject1); InvocationHandler handler2=new dynamicSubject(realsubject2); Class<?>classType1=handler1.getClass(); Class<?>classType2=handler2.getClass(); //生成两个代理类的实例 //classLoader与每一个class是相互绑定的 //dynamic subject 类只用实现一次就可以了可以把不同的handler放进去生成不同的代理类 //被代理的类 除了执行本身各自的方法之外 都通过代理类进行了方法的扩展 subject subjectproxy1=(subject)Proxy.newProxyInstance(classType1.getClassLoader(), realsubject1.getClass().getInterfaces(), handler1); subject subjectproxy2=(subject)Proxy.newProxyInstance(classType2.getClassLoader(), realsubject2.getClass().getInterfaces(), handler2); subjectproxy1.doMethod("function1"); subjectproxy1.doMethod("function2"); subjectproxy2.doMethod("function1"); subjectproxy2.doMethod("function2"); } } //最后的输出的结果: /* before excusion of the domethed excute the subject in method1:function1 after excusion of the domethed Object arg0:class com.sun.proxy.$Proxy0 Method arg1:doMethod Object[] arg2:class java.lang.String before excusion of the domethed excute the subject in method1:function2 after excusion of the domethed Object arg0:class com.sun.proxy.$Proxy0 Method arg1:doMethod Object[] arg2:class java.lang.String before excusion of the domethed excute the subject in method2:function1 after excusion of the domethed Object arg0:class com.sun.proxy.$Proxy0 Method arg1:doMethod Object[] arg2:class java.lang.String before excusion of the domethed excute the subject in method2:function2 after excusion of the domethed Object arg0:class com.sun.proxy.$Proxy0 Method arg1:doMethod Object[] arg2:class java.lang.String */

    具体的说明:

        1.代理类是一种Dynamic Class,所谓Dynamic Class就是这样的类:这个类不是预先就定义好的,而是在程序运行的时候生成的,在生成它的时候必须提供一组interface给这个class,该Class宣称其实现了这些interface(可以看到后面列出的Proxy类的两个方法都需要传递进来一个interface数组 这个实际使用的时候也可以通过反射机制realSubject.getClass().getInterfaces()来进行),于是根据父类就是子类的原则,可以把新生成的Class当做所传进来的任意的一个借口来使用。在这里动态生成的Proxy类的每一个实例,都有一个InvocationHandler与之相对应,这个InvocationHandler是负责实际的调用工作,比如在上面的例子中,通过Proxy实例调用domethod方法的时候,这个调用会被转到InvocationHandler的invoke方法上,实际上调用的是这invoke方法。

        2.interface InvocationHandler Object invoke(Object proxy, Method method, Object[] args)

    关于invocationhandler拦截器 主要有一个方法 invoke

    Object invoke(Object proxy,Method method,Object[] args) throws Throwable

        第一个参数proxy在理解上可以表示被代理的对象,也就是运行的时候动态生成的被代理对象的实例,即真是主题,但实际上传入的是一个与代理类相关的信息:class com.sun.proxy.$Proxy0 ,这个是与被代理对象相关联的。Method method表示被拦截的接口方法,args表示接口方法的参数。

        参照上面的结果,可以看到打印出来的参数 arg0是class com.sun.proxy.$Proxy0 表示的是传进来的代理类 。arg1是:doMethod (这个是说Method类是一个doMethod方法 通过proxy.newinstance的第二个参数传进来的) arg2是:class java.lang.String(这个是说domethod方法的参数类型,注意这里仅仅传入参数类型)

        具体的实现暂时不去深究,可以直接参考文档中对于动态代理的实现的相关描述。

        3.class Proxy
        真正表示动态代理的类,提供两个静态方法:
    Class<?>
    getProxyClass(ClassLoader loader, Class<?>[] interface)
        主要用到的是这个:
        Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        运行的时候会动态产生代理对象,多了InvocationHandler参数(只是InvocationHandler接口的实现类),它与代理对象关联,当请求分发到代理对象后,会自动执行h.invoke(...)方法,invoke方法就是我们用来做N多事情的地方(我们对InvocationHandler的invoke方法进行了重写),调用invoke方法的时候,代理机制会自动跳转到handler的实现的部分,去执行invoke方法,这个是java内部的机制。这一部分也用到了一些反射的性质。

        所谓的动态代理,就是只需要把不同的实现了invocationhandler接口的对象(实际主题的实现)丢进动态代理的生成方法中,通过代理类来执行相对应的方法,在java实际运行的时候,就会动态跳转到实际主题的执行过程中去

        这里本质上,与静态的主题一样,是想要实现相同的代理效果(在原来方法的基础上添加新的方法 进行扩展)但是实现的方式不同,通过invocationhandler接口结合java的反射机制,编写一个代理类即可,只有在实际执行的过程中,这个代理类才与实际的实现主体绑定在一起,这样显然更灵活,只需要更少的代码,更能体香java的反射机制的特性。

        4.最后再理一下动态代理实现的几大步骤:
        (1)创建好真实主题的接口及其具体的实现。

        (2)创建一个实现了InvocationHandler接口的类,这个类必须实现(重写)invoke方法,并且这个类要含有一个对于真实主题的引用(可以通过构造函数给引用赋值,也可以通过setter方法给引用赋值)

        (3)生成真实主题的实例,生成(2)中所创建的类的实例(注意将真实主题传进去)

        (3)由Proxy类的静态方法 new ProxyInstance(ClassLoader loader,class[]interfaces,InvocationHandler h)创建代理类。

        (4)通过代理类来调用方法,方法名与真实主题的方法名一致。

        其实对比静态模式和类似的装饰者模式的实现机制就可以发现动态代理的特点

        1、 动态代理的realSubject实际主题类实际上继承的是invocationner接口

        2、 并且实现了其中的invoke方法,它所拥有的属性是Object subject类型的,并不是想静态代理或者装饰者模式那样,维护的引用是其所继承的抽象subject(或者是component)类型。

        这样一来,动态代理的dynamicSubject类相当与是一个”万能subject”可以与任何实际的代理绑定,通过java中的Proxy类中的静态方法也可以生成对应的代理对象(也就是所谓的在执行的时候自动生成代理),不用为了某一个特定的代理对象而单独去写一个代理的实现类。

    介绍spring的很好的文章资料:

    http://my.oschina.net/myriads/blog/37922

    在Spring框架中的AOP方法就是用的动态代理,但是实现起来比用Java Reflection Api要简单许多,原理上基本是一样的。

    感觉代理模式要用到反射的一些知识,具体参考一下这个

    (http://www.cnblogs.com/Goden/p/3788808.html),特别注意Method类的invoke方法。

    关于类的加载 进一步可以参考这个:

    http://www.ibm.com/developerworks/cn/java/j-lo-classloader/index.html

    上面的大量内容都参考了 圣思园的视频

  • 相关阅读:
    福尔摩斯的约会 (20)
    数素数 (20)
    STL源码分析-priority_queue
    STL源码分析-bitset
    STL源码分析-rbtree
    STM32自动生成精美图案
    STL源码分析-function
    STL源码分析-list
    STL源码分析-iterator
    STL源码分析-traits
  • 原文地址:https://www.cnblogs.com/Goden/p/3933364.html
Copyright © 2020-2023  润新知