精 灵 王


  • 首页

  • 文章归档

  • 所有分类

  • 关于我

  • 搜索
设计模式之美 分布式 Redis 并发编程 个人成长 周志明的软件架构课 架构 单元测试 LeetCode 工具 位运算 读书笔记 操作系统 MySQL 异步编程 技术方案设计 集合 设计模式 三亚 游玩 转载 Linux 观察者模式 事件 Spring SpringCloud 实战 实战,SpringCloud 源码分析 线程池 同步 锁 线程 线程模型 动态代理 字节码 类加载 垃圾收集器 垃圾回收算法 对象创建 虚拟机内存 内存结构 Java

Java 字节码生成技术与JDK动态代理的实现

发表于 2020-10-10 | 分类于 Java虚拟机 | 0 | 阅读次数 146

简介

“字节码生成”并不是什么高深的技术,我们能想到的常用字节码类库有Javassist、CGLib、ASM等,其实JDK里面的javac命令才是字节码生成技术的“祖宗”。JDK8版本的javac的源代码在OpenJDK上可以看到。

使用字节码生成技术的例子有很多,比如Web服务器的JSP编译器,编译时植入的AOP框架,还有很多常用的动态代理技术,甚至使用反射时虚拟机也有可能会在运行时生成字节码来提升执行速度。

动态代理的实现

在JAVA中使用动态代理有两种常用的方式:一是使用JDK的原生动态代理,二是使用CGLib来实现动态代理。

这里主要说下JDK的动态代理实现。
动态代理所谓的“动态”,是针对使用JAVA代码实际编写了代理类的“静态”代理而言的,它的优势不是在于省去了编写代理类那一点的工作量,而是实现了可以在原始类和接口还未知的情况下,就能确定代理类的代理行为,当代理类与原始类脱离直接关系后,就可以很灵活地重用于不同的引用场景中。

动态代理用法实例:

public class TestDynamicProxy{
    interface IHello{
        void sayHello();
    }
    static class Hello implements IHello{
        @Override
        public void sayHello(){
            System.out.println("Hello World!");
        }
    }
    static class DynamicProxy implements InvocationHandler{
        private Object object;
        public Object build(Object object){
            this.object = object;
            Class c = object.getClass();
            // 返回指定类的代理类示例
            return Proxy.newProxyInstance(c.getClassLoader(),c.getInterfaces(),this);
        }
        @Override
        public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
            // 代理类的代理行为,被代理类的行为未知情况下也可以对被代理类进行行为增强
            System.out.println("welcome");
            return method.invoke(object,args);
        }
    }

    public static void main(String[] args){
        IHello hello = (IHello) new DynamicProxy().build(new Hello());
        hello.sayHello();
    }
}

启动时加入命令:-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 可以将运行时产生的动态代理类保存下来,最终磁盘中会保存一个名为“$Proxy0.class”的代理类Class文件。打开这个文件后可以看到以下内容:

final class $Proxy0 extends Proxy implements IHello {
    private static Method m1;
    private static Method m3;
    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 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("net.admol.jingling.demo.dynamic.TestDynamicProxy$IHello").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());
        }
    }
}

从这个代理类可以看出,这个代理类被final 修饰, 继承了Proxy类(这也是为什么使用JDK代理时必须要用接口,而不能用普通类),里面有4个属性m0、m1、m2、m3,分别对应hashCode()、equals()、toString()、sayHello()四个方法。并且统一调用了supper.h.invoke() (super.h就是父类的InvocationHandler实例)方法,所以无论调用动态代理的哪一个方法,实际上都是在执行InvocationHandler.invoke方法中的代理逻辑。

精 灵 王 wechat
👆🏼欢迎扫码关注微信公众号👆🏼
  • 本文作者: 精 灵 王
  • 本文链接: https://jinglingwang.cn/archives/bytecode
  • 版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
# 设计模式之美 # 分布式 # Redis # 并发编程 # 个人成长 # 周志明的软件架构课 # 架构 # 单元测试 # LeetCode # 工具 # 位运算 # 读书笔记 # 操作系统 # MySQL # 异步编程 # 技术方案设计 # 集合 # 设计模式 # 三亚 # 游玩 # 转载 # Linux # 观察者模式 # 事件 # Spring # SpringCloud # 实战 # 实战,SpringCloud # 源码分析 # 线程池 # 同步 # 锁 # 线程 # 线程模型 # 动态代理 # 字节码 # 类加载 # 垃圾收集器 # 垃圾回收算法 # 对象创建 # 虚拟机内存 # 内存结构 # Java
Java 线程模型与线程调度
Java 内存模型(JMM)总结
  • 文章目录
  • 站点概览
精 灵 王

精 灵 王

青春岁月,以此为伴

85 日志
14 分类
43 标签
RSS
E-mail
Creative Commons
Links
  • 添加友链说明
© 2022 精 灵 王
渝ICP备2020013371号
0%