Javassist 字节码增强技术 使用全解析

Java中所有的类都被编译为class文件来运行,在编译完class文件之后,类不能再被显示修改,而Javassist就是用来处理编译后的class文件,它可以用来修改方法或者新增方法,并且不需要深入了解字节码,还可以生成一个新的类对象。

创建class

创建maven项目,引入Javassist库 po

<!-- https://mvnrepository.com/artifact/javassist/javassist -->
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.1.GA</version>
        </dependency>

使用javassist来创建一个CodeClass类

package com.mscloudemsh.zookeeper.test;

import javassist.*;
 
import java.io.IOException;

public class CreateCodeClass {
    public static void main(String[] args) throws CannotCompileException, IOException, NotFoundException {
        ClassPool pool=ClassPool.getDefault();

        //创建一个空类
        CtClass cc=pool.makeClass("com.mscloudemsh.zookeeper.test.Person");

        //2 新增一个字段
        CtField param=new CtField(pool.get("java.lang.String"),"name",cc);

        //访问级private
        param.setModifiers(Modifier.PRIVATE);
        cc.addField(param);

        //3生成getter\setter方法
        cc.addMethod(CtNewMethod.setter("setName",param));
        cc.addMethod(CtNewMethod.getter("getName",param));

        // 4. 添加无参的构造函数
        CtConstructor cons = new CtConstructor(new CtClass[]{}, cc);
        cons.setBody("{name = \"xiaohong\";}");
        cc.addConstructor(cons);

        // 5. 添加有参的构造函数
        cons = new CtConstructor(new CtClass[]{pool.get("java.lang.String")}, cc);
        // $0=this / $1,$2,$3... 代表方法参数
        cons.setBody("{$0.name = $1;}");
        cc.addConstructor(cons);


        CtMethod ctMethod=new CtMethod(CtClass.voidType,"printName",new CtClass[]{},cc);
        ctMethod.setModifiers(Modifier.PUBLIC);
        ctMethod.setBody("{System.out.println(name);}");

        cc.addMethod(ctMethod);

        cc.writeFile("/Users/kevin/Downloads/zookeeper-source-desc/src/main/java");
    }
}

执行完之后生成了CodeClass.class

使用方法

从上文的demo中可以看到部分使用方法,在javassist中CtClass代表的就是类class,ClassPool就是CtClass的容器,ClassPool维护了所有创建的CtClass对象,需要注意的是当CtClass数量过大会占用大量内存,需要调用CtClass.detach()释放内存。

ClassPool重点有以下几个方法:
1. getDefault() 单例获取ClassPool
2. appendClassPath() 将目录添加到ClassPath
3. insertClassPath() 在ClassPath插入jar
4. get() 根据名称获取CtClass对象
5. toClass() 将CtClass转为Class 一旦被转换则不能修改
6. makeClass() 创建新的类或接口

更多移步官方文档:http://www.javassist.org/html/javassist/ClassPool.html

CtClass需要关注的方法:
1. addConstructor() 添加构造函数
2. addField() 添加字段
3. addInterface() 添加接口
4. addMethod​() 添加方法
5. freeze() 冻结类使其不能被修改
6. defrost() 解冻使其能被修改
7. detach() 从ClassPool中删除类
8. toBytecode() 转字节码
9. toClass() 转Class对象
10. writeFile() 写入.class文件
11. setModifiers​() 设置修饰符

移步:http://www.javassist.org/html/javassist/CtClass.html

CtMethod继承CtBehavior,需要关注的方法:
1. insertBefore 在方法的起始位置插入代码
2. insterAfter 在方法的所有 return 语句前插入代码
3. insertAt 在指定的位置插入代码
4. setBody 将方法的内容设置为要写入的代码,当方法被 abstract修饰时,该修饰符被移除
5. make 创建一个新的方法

更多移步:http://www.javassist.org/html/javassist/CtBehavior.html

在setBody()中我们使用了$符号代表参数

// $0代表this $1代表第一个传入的参数 类推
printName.setBody("{System.out.println($0.name);}");

使用CtClass生成对象

上文我们生成了一个ctClass对象对应的是CodeClass.class,怎么调用CodeClass类生成对象、调用属性或方法?

 采用一种常用的方式

 

通过接口调用

新建一个接口CodeClassI,将CodeClass类的方法全部抽象出来

package com.mscloudemsh.zookeeper.test;

import com.mscloudmesh.log.CodeClassI;
import javassist.*;

public class InvokerDemo {
    public static void main(String[] args) throws NotFoundException, CannotCompileException, IllegalAccessException, InstantiationException {
        ClassPool pool = ClassPool.getDefault();
        pool.appendClassPath
                ("/Users/kevin/Downloads/zookeeper-source-desc/src/main/java/com/mscloudmesh/log");
        //获取接口
        CtClass codeClassI=pool.get("com.mscloudmesh.log.CodeClassI");
        //获取上面生成的类
        CtClass ctClass=pool.get("com.mscloudmesh.log.CodeClass");
        ctClass.setInterfaces(new CtClass[]{codeClassI});


        //在execute方法前后加入自定义的逻辑
        CtMethod executeMethod = ctClass.getDeclaredMethod("execute");
        executeMethod.insertBefore("System.out.println(\"--开始\");");
        executeMethod.insertAfter( "System.out.println(\"--结束\");");


        //通过接口直接调用
        CodeClassI classI= (CodeClassI) ctClass.toClass().newInstance();
        System.out.println(classI.getName());
        classI.setName("mr kevin");



        classI.execute();
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值