• Java 代理模式(二) Java中的动态代理


    动态代理类

      Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

      1.Interface InvocationHandler

      该接口中仅定义了一个方法:

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

      在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组(无参时设置为null)。

      这个抽象方法在代理类中动态实现。

      2.Proxy

      该类即为动态代理类,作用类似于上文例子中的ProxySubject,其中主要包含如下内容:

      protected  Proxy(InvocationHandler h): 构造函数,用于给内部的invocation handler赋值。

      static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) : loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

      static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)  :返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类在Subject接口中声明过的方法)。

     

    动态代理类说明

      所谓Dynamic Proxy是这样一种class:

      它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。

      你当然可以把该class的实例当作这些interface中的任何一个来用。

      当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

      在使用动态代理类时,我们必须实现InvocationHandler接口。每一个动态代理类都会有一个与之关联的invocation handler。

      真正的调用是在invocation handler的invoke()方法里完成的。

    实例说明:

      首先定义抽象角色和真实角色类:

    public interface Subject
    {
        public void request();
    }
    复制代码
    public class RealSubject implements Subject
    {
        @Override
        public void request()
        {
            System.out.println("From real subject!");
        }
    }
    复制代码

      之后定义一个DynamicSubject类:

    复制代码
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * 该代理类的内部属性是Object类型,实际使用的时候通过该类的构造方法传递进来一个对象。
     * 该类实现了invoke()方法,该方法中的method.invoke()其实就是调用被代理对象的将要执行的方法,
     * 方法参数sub表示该方法从属于sub。
     * 通过动态代理类,我们可以在执行真实对象的方法前后加入自己的一些额外方法
     *
     */
    public class DynamicSubject implements InvocationHandler
    {
    
        //对真实对象的引用
        private Object sub;
        
        public DynamicSubject(Object obj)
        {
            this.sub = obj;
            
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable
        {
            System.out.println("Before calling: " + method);
            
            //通过反射来调用方法
            method.invoke(sub, args);
            
            System.out.println("After calling: " + method);
            return null;
        }
    
    }
    复制代码

      使用的时候:

    复制代码
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    public class Client
    {
        public static void main(String[] args)
        {
            RealSubject realSubject = new RealSubject();
    
            InvocationHandler handler = new DynamicSubject(realSubject);
    
            Class<?> classType = handler.getClass();
    
            // 生成代理
            // 动态生成一个类(实现了指定的接口),生成类的对象,转换成接口类型
            Subject subject = (Subject) Proxy.newProxyInstance(classType
                    .getClassLoader(), realSubject.getClass().getInterfaces(),
                    handler);
    
            subject.request();
            // 调用方法时,转移给handler接管,由其中的invoke()方法实际完成方法执行
    
            System.out.println(subject.getClass());// 打印出:class $Proxy0
            // $Proxy0是在运行期间动态生成的一个类
    
        }
    
    }
    复制代码

      通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。

     

    动态代理

      动态代理是指客户通过代理类来调用其他对象的方法。

      动态代理使用场合:

        调试。

        远程方法调用(RMI)。

     

    动态代理步骤

      1.创建一个实现接口InvocationHandler的类,它必须实现invoke()方法。

      2.创建被代理的类以及接口。

      3.通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)创建一个代理。

      4.通过代理调用方法。

    动态代理实现例子2:  

    动态代理模式实例2

    动态代理实现例子3:

      这个例子中定义了一个接口: 

    public interface Foo
    {
        public void doAction();
    }

      这个接口有两个实现类:

    复制代码
    public class FooImpl1 implements Foo
    {
        @Override
        public void doAction()
        {
            System.out.println("From Implement 1 !");
        }
    }
    
    public class FooImpl2 implements Foo
    {
        @Override
        public void doAction()
        {
            System.out.println("From Implement 2 !");
        }
    }
    复制代码

      定义invocation handler,其中的set方法使得实际对象是可更换的:

    复制代码
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class CommonInvocationHandler implements InvocationHandler
    {
        private Object target;
    
        public CommonInvocationHandler()
        {
    
        }
    
        public CommonInvocationHandler(Object obj)
        {
            this.target = obj;
        }
    
        public void setTarget(Object target)
        {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable
        {        
            return method.invoke(target, args);
        }
    
    }
    复制代码

      使用:

    复制代码
    import java.lang.reflect.Proxy;
    
    public class Demo
    {
        public static void main(String[] args)
        {
            CommonInvocationHandler handler = new CommonInvocationHandler();
    
            Foo f = null;
    
            handler.setTarget(new FooImpl1());
    
            f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                    new Class[] { Foo.class }, handler);
            
            f.doAction();
            System.out.println("----------------------------");
            handler.setTarget(new FooImpl2());    
            f.doAction();
    
        }
    
    }
    复制代码

      程序运行后输出:

    From Implement 1 !
    ----------------------------
    From Implement 2 !

  • 相关阅读:
    ubuntu密码正确,却不能登录图形界面
    【转】ubuntu右键在当前位置打开终端
    一些值得学习的Unity教程 (很实用的包括源码)
    Git 报错:git
    Unity3D面试——真实的面试,unity3d面试
    拖拽以及常用的鼠标事件
    白话经典算法系列之一 冒泡排序的三种实现
    c#封装三维向量,另外也看了下别人的C++封装
    c#面试3(选择题)
    Unity3D中目标相对自身的前后左右方位判断
  • 原文地址:https://www.cnblogs.com/zhaoxinshanwei/p/6689535.html
Copyright © 2020-2023  润新知