JAVAssist字节码操作

Java 动态性的两种常见实现方式

  • 字节码操作
  • 反射

运行时操作字节码可以让我们实现如下功能:

  • 动态生成新的类
  • 动态改变某个类的结构(添加/删除/修改新的属性/方法)

优势:

  • 比反射开销小,性能高
  • JAVAa sist 性能高于反射,低于ASM

常见的字节码操作类库

BCEL

这是Apache Software Fundation 的jakarta 项目的一部分。BCEL 是javaclassworking 广泛使用的一种跨级啊,它可以让你深入JVM 汇编语言进行类的操作的细节。BCEL 与javassist 所强调的是源码级别的工作。

ASM

是一个轻量及java 字节码操作框架,直接涉及到JVM 底层的操作和指令。

CGLIB

是一个强大的,高性能,高质量的Code 生成类库,基于ASM 实现。

JAVAssist

JAVAssist 库的API

  • java ssist 最外层的API 和Java 的反射包中的API 颇为类似、
  • 它主要有Ct Class,CtMethod,以及CtField 及各类组成。用以执行和JDK 反射API 中java.lang.Class ,java.lang.reflect.Method ,java.lang.reflect.Method.Field 相同的操作。

官方介绍:

Javassist (Java Programming Assistant) makes Java bytecode manipulation simple. It is a class library for editing bytecodes in Java; it enables Java programs to define a new class at runtime and to modify a class file when the JVM loads it. Unlike other similar bytecode editors, Javassist provides two levels of API: source level and bytecode level. If the users use the source-level API, they can edit a class file without knowledge of the specifications of the Java bytecode. The whole API is designed with only the vocabulary of the Java language. You can even specify inserted bytecode in the form of source text; Javassist compiles it on the fly. On the other hand, the bytecode-level API allows the users to directly edit a class file as other editors.

java ssist 库的简单使用

  • 使用一个全新的类
  • 使用XJAD 反编译工具,将生成的class 二年间反编译成JAVA 文件

例1:

javassist 库的API

  • 方法操作
  • 修改已有方法的方法体(插入代码到已有方法体)
  • 新增方法
  • 删除方法

各种符号替代

$0,$1, $2, …

this and actual parameters

$args

An array of parameters. The type of $args is Object[].

$$

All actual parameters.

For example,m($$) is equivalent to m($1,$2,…)

$cflow(…)

cflow variable

$r

The result type. It is used in a cast expression.

$w

The wrapper type. It is used in a cast expression.

$_

The resulting value

$sig

An array of java.lang.Class objects representing the formal parameter types.

$type

A java.lang.Class object representing the formal result type.

$class

A java.lang.Class object representing the class currently edited.

例2:

public class Demo02 {
    /**
     * 处理类的基本用法
     * @throws Exception
     */
    public static void test01() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");

        byte[] bytes = cc.toBytecode();
        System.out.println(Arrays.toString(bytes));

        System.out.println(cc.getName()); //获取类名
        System.out.println(cc.getSimpleName()); //获取简要类名
        System.out.println(cc.getSuperclass()); //获得父类
        System.out.println(cc.getInterfaces()); //获得接口

    }

    /**
     * 测试产生新的方法
     * @throws Exception
     */
    public static void test02() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.bjsxt.test.Emp");

//        CtMethod m = CtNewMethod.make("public int add(int a,int b){return a+b;}", cc);

        CtMethod m = new CtMethod(CtClass.intType,"add",
                new CtClass[]{CtClass.intType,CtClass.intType},cc);
        m.setModifiers(Modifier.PUBLIC);
        m.setBody("{System.out.println(\"Hello!!!\");return $1+$2;}");

        cc.addMethod(m);

        //通过反射调用新生成的方法
        Class clazz = cc.toClass();
        Object obj = clazz.newInstance();  //通过调用Emp无参构造器,创建新的Emp对象
        Method method = clazz.getDeclaredMethod("add", int.class,int.class);
        Object result = method.invoke(obj, 200,300);
        System.out.println(result);
    }

    /**
     * 修改已有的方法的信息,修改方法体的内容
     * @throws Exception
     */
    public static void test03() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");

        CtMethod cm = cc.getDeclaredMethod("sayHello",new CtClass[]{CtClass.intType});
        //方法执行前
        cm.insertBefore("System.out.println($1);System.out.println(\"start!!!\");");
        cm.insertAt(9, "int b=3;System.out.println(\"b=\"+b);");
        //方法执行后
        cm.insertAfter("System.out.println(\"after!!!\");");

        //通过反射调用新生成的方法
        Class clazz = cc.toClass();
        Object obj = clazz.newInstance();  //通过调用Emp无参构造器,创建新的Emp对象
        Method method = clazz.getDeclaredMethod("sayHello", int.class);
        method.invoke(obj, 300);
    }

    /**
     * 属性的操作
     * @throws Exception
     */
    public static void test04() throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");

//        CtField f1 = CtField.make("private int empno;", cc);
        CtField f1 = new CtField(CtClass.intType,"salary",cc);
        f1.setModifiers(Modifier.PRIVATE);
        cc.addField(f1);

//        cc.getDeclaredField("ename");   //获取指定的属性

        //增加相应的set和get方法
        cc.addMethod(CtNewMethod.getter("getSalary", f1));;
        cc.addMethod(CtNewMethod.getter("setSalary", f1));;

    }

    /**
     * 构造方法的操作
     * @throws Exception
     */
    public static void test05() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.sinosoft.test.Emp");

        CtConstructor[] cs = cc.getConstructors();
        for (CtConstructor c : cs) {
            System.out.println(c.getLongName());
        }
    }

    /**
     * 注解操作
     * @throws Exception
     */
    public static void test06() throws Exception{
         CtClass cc = ClassPool.getDefault().get("com.sinosoft.test.Emp");
         Object[] all = cc.getAnnotations();
         Author a = (Author)all[0];
         String name = a.name();
         int year = a.year();
         System.out.println("name: " + name + ", year: " + year);

    }

    public static void main(String[] args) throws Exception {
        test06();
    }
}

/**
 * 注解类
 *
*/
public @interface Author {
          String name();
           int year();
}

局限性:

  • JDK5.0 行语法不支持(包括泛型、枚举),不支持注解修改,但可以通过底层的java ssist 类来解决,具体参考javassist.bytecode.annotation
  • 不支持数组的初始化,如String[]{“1 “,”2 “},除非只有数组的容量为1
  • 不支持内部类和匿名类
  • 不支持continue 和btreak 表达式
  • 对于继承关系,有些不支持,例如:

Original: https://www.cnblogs.com/dooor/p/5289326.html
Author: Dvomu
Title: JAVAssist字节码操作

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/609444/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 三分钟永久激活WebStorm、PHPStorm、PyCharm、IntelliJ IDEA等JetBrains系列IDE

    前言 本文讲解如何永久激活WebStorm、PHPStorm、PyCharm、IntelliJ IDEA等JetBrains系列IDE,亲测有效,操作简单,一次激活,永久有效,一劳…

    Linux 2023年6月7日
    0109
  • Python:给定一个不超过5位的正整数,判断有几位

    方法一:作比较 方法二:使用整除实现,除完后如果是个0或不是个0,这种方法引入了计算,效率会降低,所以能加就不要减,能乘就不要除,能不计算就不计算 方法三: 方法四:字符串处理实现…

    Linux 2023年6月7日
    089
  • JS 模块化- 04 CMD 规范与 Sea JS

    1 CMD 规范介绍 CMD: Common Module Definition, 通用模块定义。与 AMD 规范类似,也是用于浏览器端,异步加载模块,一个文件就是一个模块,当模块…

    Linux 2023年6月6日
    098
  • 对比nushell和powershell

    2021-07-17 第一版 有些对比领域缺失,这篇文章会持续更新。 这是一篇对比powershell和nushell的文章。我是powerhsell专家,又是nushell新手,…

    Linux 2023年6月14日
    0102
  • 1:文件与目录

    CD 切换当前工作目录 mkdir 创建目录 re -dir 删除目录 pwd 打印当前工作目录 绝对路径和相对路径 硬链接 和软链接 CP拷贝 MV 移动 dirname 和 b…

    Linux 2023年6月7日
    0135
  • JuiceFS 缓存预热详解

    缓存预热是一个比较常见的概念,相信很多小伙伴都有所了解。对于 JuiceFS 来说,缓存预热就是将需要操作的数据预先从对象存储拉取到本地,从而获得与使用本地存储类似的性能表现。 缓…

    Linux 2023年6月14日
    085
  • 统计算法_探索性统计

    最近不知道写什么了,基本python的各种功能百度一下,都能搜到一大把,最近itchat好像很火,不过对这个不是很感冒,等以后有兴趣或者用的上的时候研究研究准备把统计方面的东西再看…

    Linux 2023年6月6日
    081
  • 需求分析到软件设计复习

    什么是需求分析? 需求分析就是需求分析师对用户期望的软件行为进行表述。 谁来表述 -> 需求分析师 谁有期望-> 用户 期望什么?-> 期望的软件行为 怎样表述?…

    Linux 2023年6月8日
    096
  • 每周一个linux命令(netstat)

    基础环境 netstat 命令介绍 打印网络连接、路由表、接口统计信息、伪装连接和多播成员,使用最多的是打印网络连接信息。 netstat 命令安装 yum install net…

    Linux 2023年6月8日
    088
  • linux ssh连接自动断开问题

    场景描述:云上的虚拟机使用public ip连接ssh时,一直提示已经连接,但是就会自动关闭 通过正常虚拟机作为跳板,能够连接到目标机子上,检查发现进程正常,但是就一直连接不上 发…

    Linux 2023年6月7日
    079
  • 防火墙NAT配置与DHCP下发

    该实验如果有做的不足的地方请见谅 实验目标: 按要求划分区域,公司内部办公区为trust,服务器区为dmz,外部网络为untrust。 PC1和PC2为公司内部办公区,需要从防火墙…

    Linux 2023年6月7日
    0103
  • windows版本rabbitmq安装及日志level设置

    1.DirectX Repair 安装缺失的C++组件,不安装缺失的组件会造成第二部安装erl文件夹缺少bin文件夹2.安装otp_win64_23.1 1.配置 ERLANG_H…

    Linux 2023年6月7日
    0172
  • 模板层

    过滤器 语法结构 {{ 数据对象|过滤器名称:参数 }} 过滤器最多只能额外传输一个参数 常见过滤器 标签 注意事项 在django模板语法中写标签的时候,只需要写关键字然后tab…

    Linux 2023年6月7日
    088
  • 误删除系列二:恢复已经删除文件

    背景:基于对恢复的好奇心,所以写一系列相关的博客,在linux没有回收站这一说法,通过rm -rf file的操作,如何恢复 以下的讨论分为两种情况: 删除后进程还能找到情况 删除…

    Linux 2023年6月7日
    067
  • Tomcat 实现双向SSL认证

    大概思路: 使用openssl生产CA证书,使用keytool生产密钥库 1、生成CA密钥 genrsa [产生密钥命令] -des3 [加密算法] -out[密钥文件输出路径] …

    Linux 2023年6月14日
    067
  • 域控制器所需的DNS SRV记录没有在DNS中注册的解决方法

    搭建完AD和DNS之后,发现在DNS的正向查找区域没有SRV记录,并且客户端无法加入到AD中,如下 解决方法 删除正向查找区域下的目录 然后选择”正向查找区域&#822…

    Linux 2023年6月14日
    094
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球