Java工具篇之Javassist字节码编程

简介: Javassist是一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码。相对于bcel, asm等这些工具,开发者不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。javassist简单易用, 快速。
Javassist是一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码。
相对于bcel, asm等这些工具,开发者不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。javassist简单易用, 快速。

一、核心工具类

核心类 解释
ClassPool javassist的类池,使用ClassPool 类可以跟踪和控制所操作的类,它的工作方式与 JVM 类装载器非常相似
CtClass CtClass提供了类的操作,如在类中动态添加新字段、方法和构造函数、以及改变类、父类和接口的方法。
CtField 类的属性,通过它可以给类创建新的属性,还可以修改已有的属性的类型,访问修饰符等
CtMethod 类中的方法,通过它可以给类创建新的方法,还可以修改返回类型,访问修饰符等, 甚至还可以修改方法体内容代码
CtConstructor 与CtMethod类似

二、API

2.1 ClassPool

    // 类库, jvm中所加载的class
     ClassPool pool = ClassPool.getDefault();
    // 加载一个已知的类, 注:参数必须为全量类名
    CtClass ctClass = pool.get("com.itheima.Student");
    // 创建一个新的类, 类名必须为全量类名
    CtClass tClass = pool.makeClass("com.itheima.Calculator");
AI 代码解读

2.2 CtField

    // 获取已知类的属性
    CtField ctField = ctClass.getDeclaredField("name");
    // 构建新的类的成员变量
    CtField ctFieldNew = new CtField(CtClass.intType,"age",ctClass);
    // 设置类的访问修饰符为public
    ctFieldNew.setModifiers(Modifier.PUBLIC);
    // 将属性添加到类中
    ctClass.addField(ctFieldNew);
AI 代码解读

2.3 CtMethod

    // 获取已有方法
    //创建新的方法, 参数1:方法的返回类型,参数2:名称,参数3:方法的参数,参数4:方法所属的类
    CtMethod ctMethod = new CtMethod(CtClass.intType, "calc", new CtClass[]
{CtClass.intType,CtClass.intType}, tClass);
    // 设置方法的访问修饰
    ctMethod.setModifiers(Modifier.PUBLIC);
    // 将新建的方法添加到类中
    ctClass.addMethod(ctMethod);
    // 方法体内容代码 $1代表第一个参数,$2代表第二个参数
    ctMethod.setBody("return $1 + $2;"); 

    CtMethod ctMethod = ctClass.getDeclaredMethod("sayHello");
AI 代码解读

2.4 CtConstructor

    // 获取已有的构造方法, 参数为构建方法的参数类型数组
    CtConstructor ctConstructor = ctClass.getDeclaredConstructor(new CtClass[]{});
    // 创建新的构造方法
    CtConstructor ctConstructor = new CtConstructor(new CtClass[]{CtClass.intType},ctClass); ctConstructor.setModifiers(Modifier.PUBLIC);
    ctConstructor.setBody("this.age = $1;");
    ctClass.addConstructor(ctConstructor);
    // 也可直接创建
    ctConstructor = CtNewConstructor.make("public Student(int age){this.age=age;}", ctClass);
AI 代码解读

三、示例

public class User {

    private String name;

    public User() {
    }

    public User(String name) {
        this.name = name;
    }

    public String sayJavassist() {
        return "Hello Javassist";
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}
AI 代码解读

3.1 修改方法

        // 类库池, jvm中所加载的class
        ClassPool pool = ClassPool.getDefault();
        // 获取指定的Student类
        CtClass ctClass = pool.get("com.example.test.User");
        // 获取sayHello方法
        CtMethod ctMethod = ctClass.getDeclaredMethod("sayJavassist");
        // 在方法的代码后追加 一段代码
        ctMethod.insertAfter("System.out.println(\"I'm Javassist.\");");
        // 使用当前的ClassLoader加载被修改后的类
        Class<?> newClass = ctClass.toClass();
        User user = (User) newClass.newInstance();
        System.out.println(user.sayJavassist());
AI 代码解读

3.2 动态添加方法

        // 类库池, jvm中所加载的class
        ClassPool pool = ClassPool.getDefault();
        // 获取指定的Student类
        CtClass ctClass = pool.get("com.example.test.User");
        // 增加方法
        CtMethod ctMethod = new CtMethod(CtClass.intType, "getAgeSum",
                new CtClass[]{CtClass.intType, CtClass.intType}, ctClass);
        // 设置方法的访问修饰
        ctMethod.setModifiers(Modifier.PUBLIC);
        // 设置方法体代码
        ctMethod.setBody("return $1 + $2;");
        // 添加新建的方法到原有的类中
        ctClass.addMethod(ctMethod);
        // 加载修改后的类
        ctClass.toClass();
        // 创建对象
        User stu = new User();
        // 获取calc方法
        Method dMethod = User.class.getDeclaredMethod("getAgeSum", new Class[]
                {int.class, int.class});
        // 反射调用 方法
        Object result = dMethod.invoke(stu, 10, 20);
        System.out.println(result);
AI 代码解读

3.3 动态创建类

        // 类库池, jvm中所加载的class
        ClassPool pool = ClassPool.getDefault();
        // 创建一个学校类
        CtClass schoolClass = pool.makeClass("com.example.test.School");
        // 设置为公有类
        schoolClass.setModifiers(Modifier.PUBLIC);
        // 获取String类型
        CtClass stringClass = pool.get("java.lang.String");
        // 获取list类型
        CtClass listClass = pool.get("java.util.List");
        // 获取学生的类型
        CtClass userClass = pool.get("com.example.test.User");
        // 给学校添加一个校名属性
        CtField nameField = new CtField(stringClass, "schoolName", schoolClass);
        nameField.setModifiers(Modifier.PUBLIC);
        schoolClass.addField(nameField);
        // 给学校添加一个学生集合
        CtField studentList = new CtField(listClass, "users", schoolClass);
        studentList.setModifiers(Modifier.PUBLIC);
        schoolClass.addField(studentList);
        // 给学校一个空构造
        CtConstructor ctConstructor = CtNewConstructor.make("public School() " +
                "{this.schoolName=\"湖畔小学\";this.users = new java.util.ArrayList();}", schoolClass);
        schoolClass.addConstructor(ctConstructor);

        // 给学校一个addUser的方法
        CtMethod m = new CtMethod(CtClass.voidType, "addUser", new CtClass[]{userClass}, schoolClass);
        m.setModifiers(Modifier.PUBLIC);
        // 添加学生对象到students属性中, $1代表参数1
        m.setBody("this.users.add($1);");
        schoolClass.addMethod(m);

        // 给学校添加一个介绍的方法
        CtMethod introduce = new CtMethod(CtClass.voidType, "introduce", new CtClass[]{}, schoolClass);
        introduce.setBody("System.out.println(\"The School name is \" + this.schoolName);");
        introduce.insertAfter("System.out.println(this.users);");
        schoolClass.addMethod(introduce);

        // 加载修改后的学校
        Class<?> schoolLoadClass = schoolClass.toClass();
        // 构建一个学校(空构造)
        Object school = schoolLoadClass.newInstance();
        // 获取添加用户方法
        Method addUserMethod = schoolLoadClass.getDeclaredMethod("addUser", userClass.toClass());
        addUserMethod.invoke(school, new User("小明"));
        addUserMethod.invoke(school, new User("小张"));
        // 获取介绍方法,把刚才的信息给打印处理
        Method introduceMethod = school.getClass().getDeclaredMethod("introduce");
        introduceMethod.invoke(school);
AI 代码解读
The School name is 湖畔小学
[User{name='小明'}, User{name='小张'}]
AI 代码解读
目录
打赏
0
0
0
0
42
分享
相关文章
k8s的出现解决了java并发编程胡问题了
Kubernetes通过提供自动化管理、资源管理、服务发现和负载均衡、持续交付等功能,有效地解决了Java并发编程中的许多复杂问题。它不仅简化了线程管理和资源共享,还提供了强大的负载均衡和故障恢复机制,确保应用程序在高并发环境下的高效运行和稳定性。通过合理配置和使用Kubernetes,开发者可以显著提高Java应用程序的性能和可靠性。
63 31
注解的艺术:Java编程的高级定制
注解是Java编程中的高级特性,通过内置注解、自定义注解及注解处理器,可以实现代码的高度定制和扩展。通过理解和掌握注解的使用方法,开发者可以提高代码的可读性、可维护性和开发效率。在实际应用中,注解广泛用于框架开发、代码生成和配置管理等方面,展示了其强大的功能和灵活性。
63 25
Java编程中的异常处理:从基础到高级
在Java的世界中,异常处理是代码健壮性的守护神。本文将带你从异常的基本概念出发,逐步深入到高级用法,探索如何优雅地处理程序中的错误和异常情况。通过实际案例,我们将一起学习如何编写更可靠、更易于维护的Java代码。准备好了吗?让我们一起踏上这段旅程,解锁Java异常处理的秘密!
在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境
以上内容是一个简单的实现在Java后端中通过DockerClient操作Docker生成python环境并执行代码,最后销毁的案例全过程,也是实现一个简单的在线编程后端API的完整流程,你可以在此基础上添加额外的辅助功能,比如上传文件、编辑文件、查阅文件、自定义安装等功能。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
在线编程实现!如何在Java后端通过DockerClient操作Docker生成python环境
课时6:Java编程起步
课时6:Java编程起步,主讲人李兴华。课程摘要:介绍Java编程的第一个程序“Hello World”,讲解如何使用记事本或EditPlus编写、保存和编译Java源代码(*.java文件),并解释类定义、主方法(public static void main)及屏幕打印(System.out.println)。强调类名与文件名一致的重要性,以及Java程序的编译和执行过程。通过实例演示,帮助初学者掌握Java编程的基本步骤和常见问题。
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
FastExcel 是一款基于 Java 的高性能 Excel 处理工具,专注于优化大规模数据处理,提供简洁易用的 API 和流式操作能力,支持从 EasyExcel 无缝迁移。
435 9
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
105 5
Java 并发编程——volatile 关键字解析
java并发编程中Monitor里的waitSet和EntryList都是做什么的
在Java并发编程中,Monitor内部包含两个重要队列:等待集(Wait Set)和入口列表(Entry List)。Wait Set用于线程的条件等待和协作,线程调用`wait()`后进入此集合,通过`notify()`或`notifyAll()`唤醒。Entry List则管理锁的竞争,未能获取锁的线程在此排队,等待锁释放后重新竞争。理解两者区别有助于设计高效的多线程程序。 - **Wait Set**:线程调用`wait()`后进入,等待条件满足被唤醒,需重新竞争锁。 - **Entry List**:多个线程竞争锁时,未获锁的线程在此排队,等待锁释放后获取锁继续执行。
114 12
|
3月前
|
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
318 2
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####