【java】使用Java 字节码操作库(Javassist)修改已有的 Java 类或者生成新的类示例

Javassist(Java Programming Assistant)是一个开源的 Java 字节码操作库。它允许用户在运行时修改已有的 Java 类或者生成新的类。Javassist 提供了比 ASM 更高层次的 API,使得字节码操作变得更为简单和直观。

以下是使用 Javassist 的基本步骤和一些示例代码:

1. 添加依赖

对于 Maven 项目,可以在 pom.xml 文件中添加以下依赖:

<dependency>
    <groupId>org.javassist</groupId>
    <artifactId>javassist</artifactId>
    <version>3.29.0-GA</version>
</dependency>

对于 Gradle 项目,可以在 build.gradle 文件中添加以下依赖:

implementation 'org.javassist:javassist:3.29.0-GA'

对于非以上两种的集成方式,可以直接导入javassist.jar包,下载地址

2. 创建和修改类

以下是一些常见的使用场景和对应的示例代码:

创建一个新类

import javassist.*;

public class CreateClassExample {
    public static void main(String[] args) {
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass cc = pool.makeClass("org.example.Student");

            //添加属性
            CtField id = new CtField(CtClass.intType, "id", cc);
            id.setModifiers(Modifier.PRIVATE);
            cc.addField(id);
            CtField name = new CtField(pool.get("java.lang.String"), "name", cc);
            name.setModifiers(Modifier.PRIVATE);
            cc.addField(name);
            CtField age = new CtField(CtClass.intType, "age", cc);
            age.setModifiers(Modifier.PRIVATE);
            cc.addField(age);

            //添加构造方法
            CtConstructor ctConstructor = new CtConstructor(new CtClass[]{CtClass.intType, pool.get("java.lang.String"), CtClass.intType}, cc);
            ctConstructor.setBody("{this.id = $1;this.name = $2;this.age = $3;}");
            cc.addConstructor(ctConstructor);

            //添加方法
            CtMethod getId = new CtMethod(CtClass.intType, "getId", new CtClass[]{}, cc);
            getId.setBody("{return this.id;}");
            cc.addMethod(getId);
            CtMethod setId = new CtMethod(CtClass.voidType, "setId", new CtClass[]{CtClass.intType}, cc);
            setId.setBody("{this.id = $1;}");
            cc.addMethod(setId);
            CtMethod getName = new CtMethod(pool.get("java.lang.String"), "getName", new CtClass[]{}, cc);
            getName.setBody("{return this.name;}");
            cc.addMethod(getName);
            CtMethod setName = new CtMethod(CtClass.voidType, "setName", new CtClass[]{pool.get("java.lang.String")}, cc);
            setName.setBody("{this.name = $1;}");
            cc.addMethod(setName);
            CtMethod getInfo = new CtMethod(pool.get("java.lang.String"), "getInfo", new CtClass[]{CtClass.intType, pool.get("java.lang.String"), CtClass.intType}, cc);
            getInfo.setBody("{ return  new StringBuilder(\"Student:{\").append(\"id:\").append($1).append(\",name:\").append($2).append(\",age:\").append($3).append(\"}\").toString(); }");
            getInfo.setModifiers(Modifier.PRIVATE);
            cc.addMethod(getInfo);
            //写入文件
            cc.writeFile("./output");

            //使用生成的类
            Class<?> clazz = cc.toClass();
            Object obj = clazz.getConstructor(int.class, String.class, int.class).newInstance(1, "vesper", 12);
            System.out.println("改之前:name="+clazz.getMethod("getName").invoke(obj));
            clazz.getMethod("setName", String.class).invoke(obj, "alice");
            System.out.println("改之后:name="+clazz.getMethod("getName").invoke(obj));
            System.out.println("改之前:id="+clazz.getMethod("getId").invoke(obj));//读id
            clazz.getMethod("setId", int.class).invoke(obj, 24);//修改id
            System.out.println("改之后:id="+clazz.getMethod("getId").invoke(obj));//读id
            //测试使用私有的方法
            Method method = clazz.getDeclaredMethod("getInfo", int.class, String.class, int.class);
            method.setAccessible(true);
            System.out.println(method.invoke(obj, 12, "123", 22));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

修改已有类

import javassist.*;

public class ModifyClassExample {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.example.ExistingClass");

        // 修改已有方法
        CtMethod method = cc.getDeclaredMethod("existingMethod");
        method.insertBefore("{ System.out.println(\"Before method call\"); }");
        method.insertAfter("{ System.out.println(\"After method call\"); }");

        // 添加新方法
        CtMethod newMethod = new CtMethod(CtClass.voidType, "newMethod", new CtClass[]{}, cc);
        newMethod.setBody("{ System.out.println(\"New method\"); }");
        cc.addMethod(newMethod);

        // 将修改后的类写入文件
        cc.writeFile("./output");

        // 使用修改后的类
        Class<?> clazz = cc.toClass();
        Object instance = clazz.getDeclaredConstructor().newInstance();
        clazz.getMethod("existingMethod").invoke(instance);
        clazz.getMethod("newMethod").invoke(instance);
    }
}

3. 动态加载类

Javassist 还支持动态加载和修改类。例如,可以通过自定义类加载器来加载修改后的类:

import javassist.*;

public class DynamicClassLoaderExample {
    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.makeClass("com.example.DynamicClass");

        // 添加方法
        CtMethod method = new CtMethod(CtClass.voidType, "dynamicMethod", new CtClass[]{}, cc);
        method.setBody("{ System.out.println(\"Dynamic method\"); }");
        cc.addMethod(method);

        // 使用自定义类加载器加载类
        ClassLoader loader = new ClassLoader() {
            @Override
            protected Class<?> findClass(String name) throws ClassNotFoundException {
                try {
                    byte[] b = cc.toBytecode();
                    return defineClass(name, b, 0, b.length);
                } catch (Exception e) {
                    throw new ClassNotFoundException(e.toString());
                }
            }
        };

        Class<?> clazz = loader.loadClass("com.example.DynamicClass");
        Object instance = clazz.getDeclaredConstructor().newInstance();
        clazz.getMethod("dynamicMethod").invoke(instance);
    }
}

4. 使用代理生成器

Javassist 还提供了一个简单的代理生成器,可以用来生成动态代理类:

import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
import javassist.util.proxy.ProxyObject;

public class ProxyExample {
    public static void main(String[] args) throws Exception {
        ProxyFactory factory = new ProxyFactory();
        factory.setSuperclass(MyClass.class);

        Class<?> proxyClass = factory.createClass();
        MyClass proxyInstance = (MyClass) proxyClass.getDeclaredConstructor().newInstance();

        ((ProxyObject) proxyInstance).setHandler(new MethodHandler() {
            @Override
            public Object invoke(Object self, java.lang.reflect.Method thisMethod, java.lang.reflect.Method proceed, Object[] args) throws Throwable {
                System.out.println("Before method call");
                Object result = proceed.invoke(self, args);
                System.out.println("After method call");
                return result;
            }
        });

        proxyInstance.myMethod(); // 调用代理方法
    }

    public static class MyClass {
        public void myMethod() {
            System.out.println("Inside method");
        }
    }
}

总结

Javassist 是一个强大的字节码操作库,提供了简洁的 API 来创建和修改 Java 类。通过上述示例代码,可以看到如何使用 Javassist 创建新类、修改已有类、动态加载类以及生成代理类。更多高级功能和详细文档可以参考 Javassist 官方文档

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值