从JDK源码分析动态代理原理

     

       大家都知道像Spring AOP、MyBatis等一些优秀的框架源码里都在大量使用动态代理,动态代理可以在不改变源码结构的情况下使得方法功能可以前置增强以及后置增强。当然代理模式也是23种设计模式之一。但是今天不是讨论动态代理怎么使用,而是动态代理在JDK的底层是怎么实现的?

我们先看一个简单动态代理的例子

1、被代理对象的接口

package com.mzt.proxy;

/**
 * @author 马志涛
 */
public interface ITest {
    public void sayHello();
}

 2、被代理对象的接口的实现

package com.mzt.proxy;

/**
 * @author 马志涛
 */
public class TestImpl implements ITest {
    @Override
    public void sayHello() {
        System.out.println("this is myself");
    }
}

3、代理对象辅助类实现InvocationHandler

package com.mzt.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author 马志涛
 */
public class TestProxy  implements InvocationHandler {
    Object obj;
    public TestProxy(Object obj){
        this.obj = obj;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置增强实现...");
        method.invoke(obj , args);
        System.out.println("后置增强实现...");
        return obj;
    }
}

 4、测试客户端

package com.mzt.proxy;

import java.lang.reflect.Proxy;

/**
 * @author 马志涛
 */
public class MainTest {
    public static void main(String[] args) {
        ITest iTest = new TestImpl();
        TestProxy testProxy = new TestProxy(iTest);
        ITest test = (ITest)Proxy.newProxyInstance(ITest.class.getClassLoader(),new Class[]{ITest.class} , testProxy);
        test.sayHello();
    }
}

5、运行程序结果

6、从第5步可以看到在没有改变sayHello() 方法里代码的情况下,实现了前置增强和后置增强的实现,调试代码看代理对象test是$Proxy0的形式

思考:

我们知道一个类的完整生命周期是以下几个步骤:

Java源文件(Java文件) ----> Java字节码文件(.class文件) ----> Class对象 ----> 实例对象----- >卸载

那么问题来了

它是怎么样在内存中生成?

生成的class的文件结构是什么样的?

1、动态代理类是事先不存在Java源文件和Java字节码的,那么它是怎么样跳过这两步生成Class对象的呢?我们进入Proxy.newProxyInstance()源码一探究竟。

2、进入getProxyClass0这个方法

 3、进入proxyClassCache.get(loader,interfaces)方法

 4、进入subKeyFactory.apply(key,parameter)方法,是个接口

5、进入ProxyClassFactory这个实现,可以看出代理类为什么是$Proxy0开头的,继续往下看

 6、关键的一句代码,看注释Generate the specified proxy class,可以看出来是在这里生成字节码文件的

7、我们想要查看proxyClassFile这个字节码文件的内容就必须要将它导出一个class文件到磁盘,然后反编译可以查看其内容。以下代码是工具类,入参是代理工具类的全限名(proxyName)和被代理对象的接口(interfaces)

package com.mzt.proxy;

import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 将动态代理字节码文件输出到磁盘
 * 马志涛
 */
public class ClassOutUtil {
    public void outPutFile(String proxyName, Class interfaces){
        String paths = interfaces.getResource(".").getPath() + proxyName + ".class";
        /*
         * Generate the specified proxy class.
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, new Class[]{interfaces});

        FileOutputStream fileOutputStream = null;
        try{
            fileOutputStream = new FileOutputStream(paths);
            fileOutputStream.write(proxyClassFile);
            fileOutputStream.flush();
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ClassOutUtil classOutUtil = new ClassOutUtil();
        classOutUtil.outPutFile("com.mzt.TestProxy", ITest.class);
    }
}

 8、我们可以很神奇的看到生成了一个com.mzt.TestProxy.class文件,生成的字节码文件内容如下

 

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.mzt;

import com.mzt.proxy.ITest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class TestProxy extends Proxy implements ITest {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public TestProxy(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 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("com.mzt.proxy.ITest").getMethod("sayHello");
            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());
        }
    }
}

 9、代理类继承了Proxy类,实现了ITest接口,我们看一下sayHello()方法,原来核心是h.invoke()这个方法

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);
    }
}

10、代理类中并没有h这个对象,我们查看它的父类Proxy,原来h是父类中的InvocationHandler,也就是说利用代理类调用方法的时候,实际上是调用了辅助类中的Invoke方法,到此为止动态代理原理基本上搞懂了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值