zoukankan      html  css  js  c++  java
  • Java进阶--Java动态代理

    JDK version: 1.8

    动态代理中所说的“动态”, 是针对使用Java代码实际编写了代理类的“静态”代理而言的, 它的优势不在于省去了编写代理类那一点编码工作量, 
    而是实现了可以在原始类和接口还未知的时候, 就确定代理类的代理行为,当代理类与原始类脱离直接联系后, 就可以很灵活地重用于不同的应用场景之中。

    • 目前Java开发包中包含了对动态代理的支持, 但是其实现只支持对接口的的实现。其实现主要通过 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口。
    • Proxy 类主要用来获取动态代理对象, InvocationHandler 接口用来约束调用者实现。
    • 动态代理是很多框架和技术的基础, spring 的 AOP 实现就是基于动态代理实现的。

    Proxy类
    Proxy 提供用于创建动态代理类实例的静态方法, 它还是与之创建的所有动态代理类的超类。
    介绍一下 Proxy 类中最常用的方法 java.lang.reflect.Proxy#newProxyInstance,

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
        ...
        return cons.newInstance(new Object[]{h});
        ...
    }

    该方法返回动态代理类实例. 该方法有三个参数

    • loader: the class loader to define the proxy class. 与原始类的类加载器一致
    • interfaces: the list of interfaces for the proxy class to implement. 原始类实现的接口
    • h: InvocationHandler 实例, 动态代理类实例会调用 InvocationHandler 实例的 invoke 方法

    InvocationHandler接口
    InvocationHandler 接口中只有一个方法.

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

    该方法有三个参数

    • proxy: 动态代理类的实例
    • method: 原始类的 Method 实例
    • args: 被代理 Method 需要的参数

    下面通过编写一个简单地动态代理类举例说明:

     1 public class DomainProxyTest {
     2 
     3     interface IGreet {
     4 
     5         void sayHello();
     6 
     7         void sayHi();
     8     }
     9 
    10     static class Greet implements IGreet {
    11 
    12         @Override
    13         public void sayHello() {
    14             System.out.println("Hello World!");
    15         }
    16 
    17         @Override
    18         public void sayHi() {
    19             System.out.println("Hi there!");
    20         }
    21     }
    22 
    23     static class DynamicProxy implements java.lang.reflect.InvocationHandler {
    24 
    25         Object originalObj;
    26 
    27         Object bind(Object originalObj) {
    28             this.originalObj = originalObj;
    29             return java.lang.reflect.Proxy
    30                 .newProxyInstance(originalObj.getClass().getClassLoader(), originalObj.getClass().getInterfaces(), this);
    31         }
    32 
    33         @Override
    34         public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args) throws Throwable {
    35             System.out.println("welcome!");
    36             return method.invoke(originalObj, args);
    37         }
    38     }
    39 
    40     public static void main(String[] args) {
    41         System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    42         IGreet greet = (IGreet) new DynamicProxy().bind(new Greet());
    43         greet.sayHello();
    44         greet.sayHi();
    45     }
    46 }

    编译 Java 文件:

    javac DomainProxyTest.java

     执行 class 文件:

     

     发现生成了一个名称为 $Proxy0.class 的文件, 该文件就是 Java 动态生成的代理类. 反编译看一下其内容:

    import DomainProxyTest.IGreet;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    final class $Proxy0 extends Proxy implements IGreet {
        private static Method m1;
        private static Method m3;
        private static Method m4;
        private static Method m2;
        private static Method m0;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
            try {
                return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
    
        public final void sayHello() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final void sayHi() throws  {
            try {
                super.h.invoke(this, m4, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final String toString() throws  {
            try {
                return (String)super.h.invoke(this, m2, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final int hashCode() throws  {
            try {
                return (Integer)super.h.invoke(this, m0, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m3 = Class.forName("DomainProxyTest$IGreet").getMethod("sayHello");
                m4 = Class.forName("DomainProxyTest$IGreet").getMethod("sayHi");
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }

    代码逻辑很简单, 呼应了文章上面对动态代理的描述.

    点击链接加入QQ群: 282575808互联网技术交流群】:https://jq.qq.com/?_wv=1027&k=Iw86cqY6

  • 相关阅读:
    为什么转速环经过pi调节后输出的是电流?
    如何将mysql、php、nginx设置为开机自启动
    rm-rf /*
    2020/4/23-linux中的定时任务详解
    2020/4/20 一键部署服务
    2020/4/20-ansible一键部署nfs
    2020/4/20-backup之rsync
    2020/4/19-linux中/etc/hosts
    2020/4/18-linux中的selinux
    2020/4/18-linux中的iptables
  • 原文地址:https://www.cnblogs.com/xxoome/p/13851019.html
Copyright © 2011-2022 走看看