• java 动态代理


     

    动态代理

    所谓动态代理即是在程序中为一个或多个接口动态地生成实现类,该类被称作动态代理类;

    或为一个或多个接口动态地生成实例,该实例被称作动态代理对象。很明显,动态代理对象是动态代理类的实例。

    总之,动态代理类就是实现了某个或多个接口的类,动态代理对象就是该类的实例。动态表明是在程序运行时得到代理类或代理对象,而不是先定义一个类实现这些接口,而后程序中再使用该类。代理的意思见下文。

    使用Proxy创建动态代理类

    使用Proxy类的getProxyClass()方法可以创建动态代理类,得到动态代理类的Class对象,该方法格式如下

        public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)

    loader:类加载器

    interfaces:动态代理类要实现的一个或多个接口

    如有接口Person

    public interface Person {
    
        public void walk();
        public void say(String message);
    }

    使用上述方法创建一个实现该接口的动态代理类

    public class Test {
    
        public static void main(String[] args) {
    
            Class proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), Person.class);
        }
    }

    proxyClass即是实现Person接口的动态代理类的Class对象,通过该对象可以查看该类的方法

            Class proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), Person.class);
            Method[] methods = proxyClass.getDeclaredMethods();
            for (Method m:methods) System.out.println(m);

    输出结果如下:

    public final boolean com.sun.proxy.$Proxy0.equals(java.lang.Object)
    public final java.lang.String com.sun.proxy.$Proxy0.toString()
    public final int com.sun.proxy.$Proxy0.hashCode()
    public final void com.sun.proxy.$Proxy0.walk()
    public final void com.sun.proxy.$Proxy0.say(java.lang.String)

    可见动态代理类实现了Person接口。

    创建动态代理对象

    上述得到的动态代理类还不能直接创建动态代理对象,该类的构造函数包含参数

            Class proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), Person.class);
            Constructor[] cons = proxyClass.getConstructors();
            System.out.println(cons[0]);
    
            // 输出结果为
            // public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)

    可见,动态代理类的构造函数包含一个类型为InvocationHandler的参量,需要先创建该类的实例,才能创建动态代理对象。

    InvocationHandler

    InvocationHandler是一个接口,包含一个抽象方法

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

    proxy:   使用该InvocationHandler对象的动态代理对象

    method:动态代理对象调用的某个方法

    args:     动态代理对象调用的某个方法的所有参数

    返回值: 动态代理对象调用的某个方法的返回值

    使用Proxy创建动态代理类,尽管所得到的动态代理类实现了接口的抽象方法,但方法的具体内容并没有实现。每个方法具体执行什么内容,则依靠InvocationHandler的invoke()方法来实现,这也是为什么创建动态代理对象时需要一个InvocationHandler实例。

    每次执行代理对象的方法时,都会被替换成InvocationHandler对象的invoke()方法。

    如下示例,实现InvocationHandler接口

    public class MyHandler implements InvocationHandler{
    
        public Object invoke(Object proxy, Method method, Object[] args) {
    
            System.out.println("正在执行的方法:" + method);
            if (args == null) System.out.println("该方法无参数");
            else {
                System.out.println("该方法传入的参数为:");
                for (Object arg:args) System.out.println(arg);
            }
            return null;
        }
    }

    使用MyHandler的实例及动态代理类创建动态代理对象

        public static void main(String[] args) throws Exception{
            // 创建动态代理类
            Class proxyClass = Proxy.getProxyClass(Person.class.getClassLoader(), Person.class);
            // 创建InvocationHandler实例
            InvocationHandler handler = new MyHandler();
            // 创建动态代理对象
            Person person = (Person) proxyClass.getConstructor(InvocationHandler.class).newInstance(handler);
            person.say("Good");
        }

    输出结果为:

    正在执行的方法:public abstract void test.Person.say(java.lang.String)
    该方法传入的参数为:
    Good

    代理对象person在执行say("Good")方法时,会被替换为handler的invoke(proxy, method, args),proxy为person,method为say方法,args为"Good",invoke方法的返回值作为say方法的返回值。

    使用Proxy类的静态方法及MyHandler的实例直接创建动态代理对象

        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException

    loader:      类加载器

    interfaces:实现接口

    h:              InvocationHandler实例

        public static void main(String[] args) throws Exception {
            InvocationHandler handler = new MyHandler();
            // 直接创建动态代理对象
            Person person = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, handler);
            person.say("Good");
        }

    该方法原理与上述相同,步骤更为简单。

    动态代理的作用

    动态代理可以为一个对象(称其为原对象)生成一个代理对象,代理对象实现与原对象相同的接口,在执行原对象的方法之前或之后都可以使用代理对象做一些额外事务。

     如下示例:

    接口Hello

    public interface Hello {
        public abstract void sayHello();
    }

    类HelloImpl实现了Hello接口

    public class HelloImpl implements Hello{
        @Override
        public void sayHello() {
            System.out.println("Hello");
        }
    }

    用于创建代理对象的InvocationHandler

    public class HelloHandler implements InvocationHandler{
    
        // 原对象
        private Object target;
        public HelloHandler(Object target) {this.target = target;}
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            System.out.println("进入代理方法");
            System.out.println("调度真实对象方法之前的服务");
    
            //执行原对象方法
            Object rst = method.invoke(target, args);
    
            System.out.println("调度真实对象方法之后的服务");
            return rst;
        }
    }

    创建代理对象

        public static void main(String[] args) {
            //原对象
            Hello target = new HelloImpl();
            HelloHandler handler = new HelloHandler(target);
            //代理对象
            Hello proxy = (Hello) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
            proxy.sayHello();
        }

    输出结果如下:

    进入代理方法
    调度真实对象之前的服务
    Hello
    调度真实对象之后的服务

    target是HelloImpl类的实例,原对象。使用Proxy与InvocationHandler创建一个代理对象proxy,代理对象与原对象实现了同样的接口。

    通过代理对象,可以在原对象方法执行之前或之后进行一些额外处理。

     由此可知动态代理,”代理 “的含义,即是为原对象创建一个代理对象,通过代理对象可以控制原对象方法的执行。

  • 相关阅读:
    jQuery的简单函数
    Playwright-录制脚本进行自动化测试
    使用requests爬取图片并下载
    使用jmeter对websocket进行性能测试
    selenium定位动态元素的2种情况
    Python-Faker
    关于css中@media的基本使用方法
    selenium-浏览器窗口最大化、刷新、前进、后退
    selenium-滚动条滑动,iframe切换,切换窗口
    jmeter-阶梯式压测
  • 原文地址:https://www.cnblogs.com/deltadeblog/p/9890563.html
Copyright © 2020-2023  润新知