zoukankan      html  css  js  c++  java
  • JAVA 动态代理原理和实现

    在 Java 中动态代理和代理都很常见,几乎是所有主流框架都用到过的知识。在面试中也是经常被提到的话题,于是便总结了本文。

    Java动态代理的基本原理为:被代理对象需要实现某个接口(这是前提),代理对象会拦截对被代理对象的方法调用,在其中可以全然抛弃被代理对象的方法实现而完成另外的功能,也可以在被代理对象方法调用的前后增加一些额外的功能。

    动态代理可以为其他对象提供一个代理以控制对某个对象的访问。

    代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

    JDK 动态代理

    动态代理的核心其实就是代理对象的生成,即 Proxy.newProxyInstance(classLoader, proxyInterface, handler)。

    让我们进入newProxyInstance方法观摩下,核心代码其实就三行。

    这个方法需要三个参数:

    • ClassLoader,用于加载代理类的 Loader 类,通常这个 Loader 和被代理的类是同一个 Loader 类。
    • Interfaces,是要被代理的那些那些接口。
    • InvocationHandler,就是用于执行除了被代理接口中方法之外的用户自定义的操作,他也是用户需要代理的最终目的。用户调用目标方法都被代理到 InvocationHandler 类中定义的唯一方法 invoke 中。
    1
    2
    3
    4
    5
    6
    //获取代理类  
    Class cl = getProxyClass(loader, interfaces);  
    //获取带有InvocationHandler参数的构造方法  
    Constructor cons = cl.getConstructor(constructorParams);  
    //把handler传入构造方法生成实例  
    return (Object) cons.newInstance(new Object[] { h });

    一个典型的动态代理创建对象过程可分为以下四个步骤:

    1、通过实现InvocationHandler接口创建调用处理器 

    1
    IvocationHandler handler = new InvocationHandlerImpl(...);

    2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类

    1
    Class clazz = Proxy.getProxyClass(classLoader,new Class[]{...});

    3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型

    1
    Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

    4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入

    1
    Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

    为了简化对象创建过程,Proxy类中的newProxyInstance方法封装了2~4,只需两步即可完成代理对象的创建。

    1
    2
    Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
            new Class[]{Subject.class}, new InvocationHandlerImpl (real));

    生成的proxySubject继承Proxy类实现Subject接口。实现的Subject的方法实际是调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的方法(Object result=method.invoke(proxied,args));
    重点Proxy.newProxyInstance,源码分析,会在其他文档中单独总结记录。类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据。

    创建代理对象时序图

    创建代理对象时序图

    获取代理类

    getProxyClass(loader, interfaces)方法用于获取代理类,它主要做了三件事情:

    在当前类加载器的缓存里搜索是否有代理类,没有则生成代理类并缓存在本地JVM里。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // 缓存的key使用接口名称生成的List  
    Object key = Arrays.asList(interfaceNames);  
    synchronized (cache) {  
        do {  
    Object value = cache.get(key);  
             // 缓存里保存了代理类的引用  
    if (value instanceof Reference) {  
        proxyClass = (Class) ((Reference) value).get();  
    }  
    if (proxyClass != null) {  
    // 代理类已经存在则返回  
        return proxyClass;  
    else if (value == pendingGenerationMarker) {  
        // 如果代理类正在产生,则等待  
        try {  
    cache.wait();  
        catch (InterruptedException e) {  
        }  
        continue;  
    else {  
        //没有代理类,则标记代理准备生成  
        cache.put(key, pendingGenerationMarker);  
        break;  
    }  
        while (true);  
    }

    生成并加载代理类

    代理类的生成主要是以下这两行代码:

    1
    2
    3
    4
    //生成代理类的字节码文件并保存到硬盘中(默认不保存到硬盘)   
    proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);   
    //使用类加载器将字节码加载到内存中   
    proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);  

    代理类的生成过程

    ProxyGenerator.generateProxyClass()方法属于sun.misc包下,Oracle并没有提供源代码,但是我们可以使用
    JD-GUI这样的反编译软件打开jrelib t.jar来一探究竟,以下是其核心代码的分析。 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //添加接口中定义的方法,此时方法体为空  
    for (int i = 0; i < this.interfaces.length; i++) {  
      localObject1 = this.interfaces[i].getMethods();  
      for (int k = 0; k < localObject1.length; k++) {  
         addProxyMethod(localObject1[k], this.interfaces[i]);  
      }  
    }  
    //添加一个带有InvocationHandler的构造方法  
    MethodInfo localMethodInfo = new MethodInfo("<init>""(Ljava/lang/reflect/InvocationHandler;)V"1);  
    //循环生成方法体代码(省略)  
    //方法体里生成调用InvocationHandler的invoke方法代码。(此处有所省略)  
    this.cp.getInterfaceMethodRef("InvocationHandler""invoke""Object; Method; Object;")  
    //将生成的字节码,写入硬盘,前面有个if判断,默认情况下不保存到硬盘。  
    localFileOutputStream = new FileOutputStream(ProxyGenerator.access$000(this.val$name) + ".class");  
    localFileOutputStream.write(this.val$classFile);

    生成的代理类源码

    那么通过以上分析,我们可以推出动态代理为我们生成了一个这样的代理类。把方法doSomeThing的方法体修改为调用LogInvocationHandler的invoke方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class ProxyBusiness extends Proxy implements IBusiness, IBusiness2 {  
    private LogInvocationHandler h;  
    @Override  
    public void doSomeThing2() {  
        try {  
            Method m = (h.target).getClass().getMethod("doSomeThing2",null);  
            h.invoke(this, m, null);  
        catch (Throwable e) {  
            // 异常处理(略)  
        }  
    @Override  
    public boolean doSomeThing() {  
        try {  
           Method m = (h.target).getClass().getMethod("doSomeThing"null);  
           return (Boolean) h.invoke(this, m, null);  
        catch (Throwable e) {  
            // 异常处理(略)  
        }  
        return false;  
    }  
    public ProxyBusiness(LogInvocationHandler h) {  
        this.h = h;  
    }

    测试代理的代码如下:

    1
    2
    3
    4
    5
    6
    7
    //测试
    public static void main(String[] args) {  
        //构建AOP的Advice  
        LogInvocationHandler handler = new LogInvocationHandler(new Business());  
        new ProxyBusiness(handler).doSomeThing();  
        new ProxyBusiness(handler).doSomeThing2();  
    }

    下面看一个自定义代理的实现。

    被代理类接口

    1
    2
    3
    public interface Subject   {  
      public void doSomething();  
    }

    被代理类

    1
    2
    3
    4
    5
    6
    //目标对象
    public class RealSubject implements Subject{
      public void doSomething() {
        System.out.println( "call doSomething()" );
      }
    }

    调用处理器(切面)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class ProxyHandler implements InvocationHandler   {  
      private Object proxied;  
      public ProxyHandler( Object proxied )   {  
        this.proxied = proxied;  
      }  
      public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {  
    //在转调具体目标对象之前,可以执行一些功能处理
    System.out.println( "doSomething before" );
        //转调具体目标对象的方法
        return method.invoke( proxied, args); 
    //在转调具体目标对象之后,可以执行一些功能处理
    System.out.println( "doSomething after" );
      }
    }

    测试我们的代理实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class DynamicProxy   {  
      public static void main( String args[] )   {  
        RealSubject real = new RealSubject();  
        Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
         new Class[]{Subject.class}, new ProxyHandler(real));
        proxySubject.doSomething();
        //write proxySubject class binary data to file  
        createProxyClassFile();  
      }  
      public static void createProxyClassFile()   {  
        String name = "ProxySubject";  
        byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } );  
        try{  
          FileOutputStream out = new FileOutputStream( name + ".class" );  
          out.write( data );  
          out.close();  
        }catch( Exception e ) {  
          e.printStackTrace();  
        }  
      }  
    }

    运行结果:

    1
    2
    3
    doSomething before
    call doSomething()
    doSomething after

    Proxy 接口

    Proxy 的主要静态变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 映射表:用于维护类装载器对象到其对应的代理类缓存
    private static Map loaderToCache = new WeakHashMap();
    // 标记:用于标记一个动态代理类正在被创建中
    private static Object pendingGenerationMarker = new Object();
    // 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断
    private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());
    // 关联的调用处理器引用
    protected InvocationHandler h;
    Proxy的构造方法
    // 由于 Proxy 内部从不直接调用构造函数,所以 private 类型意味着禁止任何调用
    private Proxy() {}
    // 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用
    protected Proxy(InvocationHandler h) {this.h = h;}

    ProxySubject 源码

    创建的代理类 ProxySubject.class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    import java.lang.reflect.*;  
    public final class ProxySubject extends Proxy  implements Subject{  
        private static Method m1;  
        private static Method m0;  
        private static Method m3;  
        private static Method m2;  
        public ProxySubject(InvocationHandler invocationhandler){  
            super(invocationhandler);  
        }  
        public final boolean equals(Object obj){  
            try {  
                return ((Boolean)super.h.invoke(this, m1, new Object[] {  
                    obj  
                })).booleanValue();  
            }catch(Error _ex) {
            }catch(Throwable throwable){  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        public final int hashCode()   { 
            try  {  
                return ((Integer)super.h.invoke(this, m0, null)).intValue();  
            }catch(Error _ex) {
            }catch(Throwable throwable){  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        /*关键部分*/
        public final void doSomething()  {  
            try {  
                // Proxy类中protected InvocationHandler h;关联的调用处理器引用
                super.h.invoke(this, m3, null);  
                return;  
            }catch(Error _ex) {  
            }catch(Throwable throwable) {  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        public final String toString() {  
            try {  
                return (String)super.h.invoke(this, m2, null);  
            catch(Error _ex) {
            catch(Throwable throwable){  
                throw new UndeclaredThrowableException(throwable);  
            }  
        }  
        static{  
            try {  
                m1 = Class.forName("java.lang.Object").getMethod("equals"new Class[] {  
                    Class.forName("java.lang.Object")  
                });  
                m0 = Class.forName("java.lang.Object").getMethod("hashCode"new Class[0]);  
                m3 = Class.forName("Subject").getMethod("doSomething"new Class[0]);  
                m2 = Class.forName("java.lang.Object").getMethod("toString"new Class[0]);  
            }catch(NoSuchMethodException nosuchmethodexception)   {  
                throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
            }catch(ClassNotFoundException classnotfoundexception){  
                throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
            }  
        }  
    }

    CGLib 动态代理

    动态字节码生成。使用动态字节码生成技术实现AOP原理是在运行期间目标字节码加载后,生成目标类的子类,将切面逻辑加入到子类中,所以使用Cglib实现AOP不需要基于接口。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public static void main(String[] args) {  
        byteCodeGe();  
    }  
    public static void byteCodeGe() {  
        //创建一个织入器  
        Enhancer enhancer = new Enhancer();  
        //设置父类  
        enhancer.setSuperclass(Business.class);  
        //设置需要织入的逻辑  
        enhancer.setCallback(new LogIntercept());  
        //使用织入器创建子类  
        IBusiness2 newBusiness = (IBusiness2) enhancer.create();  
        newBusiness.doSomeThing2();  
    }  
    /** 
     * 记录日志 
     */  
    public static class LogIntercept implements MethodInterceptor {  
        @Override  
        public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {  
            //执行原有逻辑,注意这里是invokeSuper  
            Object rev = proxy.invokeSuper(target, args);  
            //执行织入的日志  
            if (method.getName().equals("doSomeThing2")) {  
                System.out.println("记录日志");  
            }  
            return rev;  
        }  
    }

    转自:https://www.xttblog.com/?p=2732

  • 相关阅读:
    PAT 1097. Deduplication on a Linked List (链表)
    PAT 1096. Consecutive Factors
    PAT 1095. Cars on Campus
    PAT 1094. The Largest Generation (层级遍历)
    PAT 1093. Count PAT's
    PAT 1092. To Buy or Not to Buy
    PAT 1091. Acute Stroke (bfs)
    CSS:word-wrap/overflow/transition
    node-webkit中的requirejs报错问题:path must be a string error in Require.js
    script加载之defer和async
  • 原文地址:https://www.cnblogs.com/incognitor/p/9759987.html
Copyright © 2011-2022 走看看