Java之Javassist对类的常用操作小结-yellowcong

本文介绍如何使用Javassist库动态创建Java类并修改现有类的方法。涵盖创建类对象、添加字段及方法、修改已有方法等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用Javassit完了一段时间,感觉挺牛逼的,可以轻松的操作java的字节码文件,这个东西相当于ASM是轻松不少了,对了记得获取已存在类对象的时候,通过类名称com.yellowcong.test.User来获取,而不是通过类对象来获取,因为通过类对象来获取,就会导致加载了两个不同版本的类在JVM中,就会报错,当CtClass 调用writeFile()、toClass()、toBytecode() 这些方法的时候,Javassist会冻结CtClass Object,对CtClass object的修改将不允许。

创建类对象

可以通过javassit来动态创建类文件,然后使用类文件对象,动态添加字段,set,get方法,以及添加自定义方法,

/**
 * 通过javassist 来自己创建类对象
 * 
 * @throws Exception
 */
public static void testCreateObject() throws Exception {
    ClassPool pool = ClassPool.getDefault();

    // 创建类
    CtClass ctclazz = pool.makeClass("com.yellowcong.test.User");

    // 创建属性 String 类型的数据不是基本数据类型,所以不能通过CtClass获取
    CtField usernameField = new CtField(pool.get(String.class.getName()), "username", ctclazz);
    // 添加set和get方法
    ctclazz.addMethod(CtNewMethod.setter("setUsername", usernameField));
    ctclazz.addMethod(CtNewMethod.getter("getUsername", usernameField));
    ctclazz.addField(usernameField);

    // 添加int类型的数据
    CtField ageField = new CtField(CtClass.intType, "age", ctclazz);
    // 添加set和get方法
    ctclazz.addMethod(CtNewMethod.setter("setAge", ageField));
    ctclazz.addMethod(CtNewMethod.getter("getAge", ageField));
    ctclazz.addField(ageField);

    // 添加构造器,不带参数的
    CtConstructor con = new CtConstructor(null, ctclazz);
    con.setModifiers(Modifier.PUBLIC);
    con.setBody("{System.out.println('init constructor');}");
    ctclazz.addConstructor(con);

    // 添加构造器,带参数的
    CtConstructor con2 = new CtConstructor(new CtClass[] { pool.get(String.class.getName()), CtClass.intType },
            ctclazz);
    con2.setModifiers(Modifier.PUBLIC);
    con2.setBody("{this.username = $1;this.age = $2;}");
    ctclazz.addConstructor(con2);

    // 添加方法
    CtMethod say = new CtMethod(CtClass.voidType, "say", null, ctclazz);
    say.setModifiers(Modifier.PUBLIC);
    say.setBody("{System.out.print(\"我是\"+this.username+\",今年\"+this.age+\"岁\");}");
    ctclazz.addMethod(say);

    Class<?> clz = ctclazz.toClass();

    // 获取构造器,实例化对象
    Object obj = clz.getConstructor(String.class, int.class).newInstance("yellowcong", 18);

    // 获取动态添加的方法
    clz.getMethod("say", null).invoke(obj, null);

    // 设定用户名称
    clz.getMethod("setUsername", String.class).invoke(obj, "doubi");

    Object ret = clz.getMethod("getUsername", null).invoke(obj, null);

    System.out.println(ret.toString());
}

更改以存在对象的类方法

在已有类的基础上,添加方法

/**
     * 更改类结构已经存在的方法
     * 在已有类的基础上,添加aop类似的操作,但是没有用到javassist的代理操作
     * @throws Exception
     */
    public static void changeClsMethod() throws Exception {
        ClassPool pool = ClassPool.getDefault();

        CtClass cls = pool.get("com.yellowcong.test.TestDemo");

        // 获取到所有的字段
        CtField[] fields = cls.getFields();

        for (CtField field : fields) {
            System.out.println(field.getName());
        }

        // 对于String类型,CtClass中,没有这个数据类型,只有基本的8中数据类型
        CtMethod method = cls.getDeclaredMethod("say", new CtClass[] { pool.get("java.lang.String") });

        // 复制方法
        CtMethod newMethod = CtNewMethod.copy(method, cls, null);

        // 修改原来方法的名称
        String methodName = method.getName() + "$Impl";
        method.setName(methodName);

        // 这个 inertBefore方法没有问题
        // method.insertBefore("System.out.println($1);say($$);System.out.println(\"start!!!\");");

        // insertAfter方法有问题
        // method.insertAfter("System.out.println(\"end!!!\");");

        // 修改方法体
        // method.setBody("System.out.println(\"你没机会了\");");

        // 修改原方法
        newMethod.setBody("{System.out.println(\"执行前\");" + methodName + "($$);System.out.println(\"执行后\");}");
        cls.addMethod(newMethod);

        Class<?> me = cls.toClass();
        Object obj = me.newInstance();
        me.getDeclaredMethod("say", String.class).invoke(obj, "test");
    }

修改类已存在的方法2

直接不要类以前的方法,然后直接覆盖,设定自己的方法体

    /**
     * 修改类里面的方法
     */
    public static void changeMethodBody() throws Exception {
        ClassPool pool = ClassPool.getDefault();

        CtClass ctClazz = pool.get("com.yellowcong.test.TestDemo");

        // 获取TestDemo里面的public void say(String name) 方法
        CtMethod method = ctClazz.getDeclaredMethod("say", new CtClass[] { pool.get(String.class.getName()) });

        //修改方法内容,这个方法已经存在,不用在添加了
        method.setBody("{System.out.print(\"你是逗比\t\"+$1);}");
        method.insertBefore("System.out.println(\"end!!!\");");

        //对于inserAfter使用不了,具体是啥原因还不清楚

        Class<?> clazz = ctClazz.toClass();

        Object obj = clazz.newInstance();

        clazz.getMethod("say", String.class).invoke(obj, "yellowcong");
    }

完整的代码

package com.yellowcong.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.NotFoundException;

/**
 *
 * 作者:yellowcong 日期:2017/09/01 時間:12:07:33 描述:
 */
public class Demo10 {

    public static void main(String[] args) throws Exception, NotFoundException {

         changeMethodBody() ;

    }

    /**
     * 修改类里面的方法
     */
    public static void changeMethodBody() throws Exception {
        ClassPool pool = ClassPool.getDefault();

        CtClass ctClazz = pool.get("com.yellowcong.test.TestDemo");

        // 获取TestDemo里面的public void say(String name) 方法
        CtMethod method = ctClazz.getDeclaredMethod("say", new CtClass[] { pool.get(String.class.getName()) });

        //修改方法内容,这个方法已经存在,不用在添加了
        method.setBody("{System.out.print(\"你是逗比\t\"+$1);}");
        method.insertBefore("System.out.println(\"end!!!\");");

        //对于inserAfter使用不了,具体是啥原因还不清楚

        Class<?> clazz = ctClazz.toClass();

        Object obj = clazz.newInstance();

        clazz.getMethod("say", String.class).invoke(obj, "yellowcong");
    }

    /**
     * 更改类结构已经存在的方法
     * 在已有类的基础上,添加aop类似的操作,但是没有用到javassist的代理操作
     * @throws Exception
     */
    public static void changeClsMethod() throws Exception {
        ClassPool pool = ClassPool.getDefault();

        CtClass cls = pool.get("com.yellowcong.test.TestDemo");

        // 获取到所有的字段
        CtField[] fields = cls.getFields();

        for (CtField field : fields) {
            System.out.println(field.getName());
        }

        // 对于String类型,CtClass中,没有这个数据类型,只有基本的8中数据类型
        CtMethod method = cls.getDeclaredMethod("say", new CtClass[] { pool.get("java.lang.String") });

        // 复制方法
        CtMethod newMethod = CtNewMethod.copy(method, cls, null);

        // 修改原来方法的名称
        String methodName = method.getName() + "$Impl";
        method.setName(methodName);

        // 这个 inertBefore方法没有问题
        // method.insertBefore("System.out.println($1);say($$);System.out.println(\"start!!!\");");

        // insertAfter方法有问题
        // method.insertAfter("System.out.println(\"end!!!\");");

        // 修改方法体
        // method.setBody("System.out.println(\"你没机会了\");");

        // 修改原方法
        newMethod.setBody("{System.out.println(\"执行前\");" + methodName + "($$);System.out.println(\"执行后\");}");
        cls.addMethod(newMethod);

        Class<?> me = cls.toClass();
        Object obj = me.newInstance();
        me.getDeclaredMethod("say", String.class).invoke(obj, "test");
    }

    /**
     * 通过javassist 来自己创建类对象
     * 
     * @throws Exception
     */
    public static void testCreateObject() throws Exception {
        ClassPool pool = ClassPool.getDefault();

        // 创建类
        CtClass ctclazz = pool.makeClass("com.yellowcong.test.User");

        // 创建属性 String 类型的数据不是基本数据类型,所以不能通过CtClass获取
        CtField usernameField = new CtField(pool.get(String.class.getName()), "username", ctclazz);
        // 添加set和get方法
        ctclazz.addMethod(CtNewMethod.setter("setUsername", usernameField));
        ctclazz.addMethod(CtNewMethod.getter("getUsername", usernameField));
        ctclazz.addField(usernameField);

        // 添加int类型的数据
        CtField ageField = new CtField(CtClass.intType, "age", ctclazz);
        // 添加set和get方法
        ctclazz.addMethod(CtNewMethod.setter("setAge", ageField));
        ctclazz.addMethod(CtNewMethod.getter("getAge", ageField));
        ctclazz.addField(ageField);

        // 添加构造器,不带参数的
        CtConstructor con = new CtConstructor(null, ctclazz);
        con.setModifiers(Modifier.PUBLIC);
        con.setBody("{System.out.println('init constructor');}");
        ctclazz.addConstructor(con);

        // 添加构造器,带参数的
        CtConstructor con2 = new CtConstructor(new CtClass[] { pool.get(String.class.getName()), CtClass.intType },
                ctclazz);
        con2.setModifiers(Modifier.PUBLIC);
        con2.setBody("{this.username = $1;this.age = $2;}");
        ctclazz.addConstructor(con2);

        // 添加方法
        CtMethod say = new CtMethod(CtClass.voidType, "say", null, ctclazz);
        say.setModifiers(Modifier.PUBLIC);
        say.setBody("{System.out.print(\"我是\"+this.username+\",今年\"+this.age+\"岁\");}");
        ctclazz.addMethod(say);

        Class<?> clz = ctclazz.toClass();

        // 获取构造器,实例化对象
        Object obj = clz.getConstructor(String.class, int.class).newInstance("yellowcong", 18);

        // 获取动态添加的方法
        clz.getMethod("say", null).invoke(obj, null);

        // 设定用户名称
        clz.getMethod("setUsername", String.class).invoke(obj, "doubi");

        Object ret = clz.getMethod("getUsername", null).invoke(obj, null);

        System.out.println(ret.toString());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

狂飙的yellowcong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值