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

2.2 CtField

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

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

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

三、示例

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 + '\'' +
                '}';
    }
}

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

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

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);
The School name is 湖畔小学
[User{name='小明'}, User{name='小张'}]
相关文章
|
5天前
|
设计模式 安全 Java
Java编程中的单例模式:理解与实践
【10月更文挑战第31天】在Java的世界里,单例模式是一种优雅的解决方案,它确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的实现方式、使用场景及其优缺点,同时提供代码示例以加深理解。无论你是Java新手还是有经验的开发者,掌握单例模式都将是你技能库中的宝贵财富。
12 2
|
7天前
|
Java API Apache
Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
【10月更文挑战第29天】Java编程如何读取Word文档里的Excel表格,并在保存文本内容时保留表格的样式?
41 5
|
2天前
|
安全 Java 编译器
JDK 10中的局部变量类型推断:Java编程的简化与革新
JDK 10引入的局部变量类型推断通过`var`关键字简化了代码编写,提高了可读性。编译器根据初始化表达式自动推断变量类型,减少了冗长的类型声明。虽然带来了诸多优点,但也有一些限制,如只能用于局部变量声明,并需立即初始化。这一特性使Java更接近动态类型语言,增强了灵活性和易用性。
79 53
|
1天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
21小时前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
21 6
|
1天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
1天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
18 1
|
5天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
6天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
31 4
|
6天前
|
消息中间件 供应链 Java
掌握Java多线程编程的艺术
【10月更文挑战第29天】 在当今软件开发领域,多线程编程已成为提升应用性能和响应速度的关键手段之一。本文旨在深入探讨Java多线程编程的核心技术、常见问题以及最佳实践,通过实际案例分析,帮助读者理解并掌握如何在Java应用中高效地使用多线程。不同于常规的技术总结,本文将结合作者多年的实践经验,以故事化的方式讲述多线程编程的魅力与挑战,旨在为读者提供一种全新的学习视角。
29 3
下一篇
无影云桌面