1.摘要

javassist是一个"Java字节码"操作类库.

2.应用场景

1. 读取.class文件到内存, 
        新增或删除字段或方法,
        修改字段或方法的 访问级别, 是否final, 是否static, 是否abstract
        改换类名,
        改换类继承的接口或父类
        ...
   然后写回.class文件.
2. ClassLoader加载某个.class文件时, 动态的在方法的前后加点字节码, 实现动态代理及AOP的功能

3.API模型

javassist提供了, 如下两种级别的API
1.高级别API
    无需"Java字节码"相关知识, 就可操作"Java字节码", 低性能, 易使用.

2.低级别API
    需要"Java字节码"相关知识, 才可操作"Java字节码", 高性能, 难使用.

源码实现上, "高级别API" 依赖了 "低级别API"

4.代码示例

高级别API的简单实用

package demo.javassist;import javassist.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;/*** 深入理解JVM字节码 - [6.2]Javassist介绍** 这本书, 主要介绍了, 高级别API的使用方式*/
@DisplayName("深入理解JVM字节码 - [6.2]Javassist介绍")
public class Demo4Book {@Test@DisplayName("创建类")public void testMakeClass() throws Throwable {ClassPool cp = ClassPool.getDefault();CtClass cc = cp.makeClass("demo.MyMain");cc.writeFile();}@Test@DisplayName("新增字段")public void testAddField() throws Throwable {ClassPool cp = ClassPool.getDefault();cp.insertClassPath(".");CtClass cc = cp.get("demo.MyMain");CtField cf = new CtField(CtClass.intType, "i", cc);cf.setModifiers(Modifier.PRIVATE);cc.addField(cf);cc.writeFile();}@Test@DisplayName("新增方法")public void testAddMethod() throws Throwable {ClassPool cp = ClassPool.getDefault();cp.insertClassPath(".");CtClass cc = cp.get("demo.MyMain");// 参数依次为 返回类型, 方法名称, 参数类型, 方法所属类CtMethod cm = new CtMethod(CtClass.intType, "foo",new CtClass[]{CtClass.intType, CtClass.intType}, cc);// 设定方法的 修饰符(public, static等等), 具体参考Modifiercm.setModifiers(Modifier.PUBLIC);cc.addMethod(cm);cc.writeFile();}@Test@DisplayName("设定方法体")public void testMethodSetBody() throws Throwable{ClassPool cp = ClassPool.getDefault();cp.insertClassPath(".");CtClass cc = cp.get("demo.MyMain");CtMethod cm = cc.getMethod("foo", "(II)I");// $0为this, $1为第一个方法参数, $2位第二个方法参数cm.setBody("{System.out.println(\"$0 = this \" + $0); return $1 + $2 + i; }");cc.writeFile();}@Test@DisplayName("方法体-前加代码")public void testMethodInsertBefore() throws Throwable{ClassPool cp = ClassPool.getDefault();cp.insertClassPath(".");CtClass cc = cp.get("demo.MyMain");CtMethod cm = cc.getMethod("foo", "(II)I");cm.insertBefore("System.out.println(\" method start2 \");");cm.insertBefore("System.out.println(\" method start1 \");");cc.writeFile();}@Test@DisplayName("方法体-后加代码")public void testMethodInsertAfter() throws Throwable{ClassPool cp = ClassPool.getDefault();cp.insertClassPath(".");CtClass cc = cp.get("demo.MyMain");CtMethod cm = cc.getMethod("foo", "(II)I");cm.insertAfter("System.out.println(\" method end1 \");");cm.insertAfter("System.out.println(\" method end2 \");");cc.writeFile();}@Test@DisplayName("$开头的特殊标识符列表")public void test$Args() throws Throwable {ClassPool cp = ClassPool.getDefault();cp.insertClassPath(".");CtClass cc = cp.get("demo.MyMain");CtMethod cm = cc.getMethod("foo", "(II)I");cm.setBody("{return 0;}");// 方法参数数组  // new Object[]{new Integer(var1), new Integer(var2)}cm.insertAfter("System.out.println(\" $args = \" + $args);");// 返回值       // 0cm.insertAfter("System.out.println(\" $_ \" + $_);");// 参数类型数组  // Desc.getParams("(II)I") // 类型描述符cm.insertAfter("System.out.println(\" $sig \" + $sig);");// 返回类型     // Desc.getType("I")cm.insertAfter("System.out.println(\" $type \" + $type);");// 当前类型     // Desc.getClazz("demo.MyMain")cm.insertAfter("System.out.println(\" $class \" + $class);");// 所有参数     // this.foo(var1, var2);cm.insertAfter("foo($$);");// $r TODO// $w TODO// $cflow 递归调用深度cc.writeFile();}@Test@DisplayName("添加一个fibonacci方法")public void testAddFibonacci() throws Throwable {ClassPool cp = ClassPool.getDefault();cp.insertClassPath(".");CtClass cc = cp.get("demo.MyMain");// 参数依次为 返回类型, 方法名称, 参数类型, 方法所属类CtMethod cm = new CtMethod(CtClass.intType, "fibonacci",new CtClass[]{CtClass.intType}, cc);// 设定方法的 修饰符(public, static等等), 具体参考Modifiercm.setModifiers(Modifier.PUBLIC);cm.setBody("if ($1 <=1) {return $1;} " +"else {return fibonacci($1-1) + fibonacci($1-2);}");cc.addMethod(cm);cc.writeFile();}@Test@DisplayName("$cflow-递归调用深度")public void test$cflow() throws Throwable {ClassPool cp = ClassPool.getDefault();cp.insertClassPath(".");CtClass cc = cp.get("demo.MyMain");// public static Cflow _cflow$0 = new Cflow();// public class Cflow extends ThreadLocal<Cflow.Depth>// 实现方式, 线程本地变量CtMethod cm = cc.getMethod("fibonacci", "(I)I");cm.useCflow("fibonacci");cm.insertBefore("if ($cflow(fibonacci) == 0) {" +"System.out.println(\" fibonacci init \" + $1);" +"}");cc.writeFile();}@Test@DisplayName("除零异常")public void testDivByZero() throws Throwable{ClassPool cp = ClassPool.getDefault();cp.insertClassPath(".");CtClass cc = cp.get("demo.MyMain");CtMethod cm = cc.getMethod("foo", "(II)I");cm.setBody("{int a = 1; int b = 0; int c = a / b; return $1 + $2;}");cc.writeFile();}@Test@DisplayName("除零异常, insertAfter finally")public void testInsertAfterFinally() throws Throwable {ClassPool cp = ClassPool.getDefault();cp.insertClassPath(".");CtClass cc = cp.get("demo.MyMain");CtMethod cm = cc.getMethod("foo", "(II)I");cm.insertAfter("System.out.println(\" $_ \" + $_);", true);cc.writeFile();}
}

低级别API的简单实用

package demo.javassist;import javassist.ClassPool;
import javassist.CtClass;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.ClassFile;
import javassist.bytecode.FieldInfo;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;import java.io.*;/*** Javassist官方参考* 1.官网: http://www.javassist.org/* 2.入门指南: http://www.javassist.org/tutorial/tutorial.html* 3.在线API文档: http://www.javassist.org/html/index.html*/
@DisplayName("Javassist官方指南")
public class Demo4Tutorial {/*** 三种获取ClassFile的方式* @throws Throwable*/@Test@DisplayName("三种获取ClassFile的方式")public void testThreeWayGetClassFile() throws Throwable {// 方式1(高级别API): 从CtClass获取ClassFileClassPool cp = ClassPool.getDefault();cp.insertClassPath(".");CtClass cc = cp.get("demo.MyMain");ClassFile cf = cc.getClassFile();// 方式2(低级别API): 直接从数据流构建ClassFileBufferedInputStream fin = new BufferedInputStream(new FileInputStream("./demo/MyMain.class"));cf = new ClassFile(new DataInputStream(fin));// 方式3(低级别API): 手动构建ClassFilecf = new ClassFile(false, "demo.Foo", null);cf.setInterfaces(new String[] { "java.lang.Cloneable" });FieldInfo f = new FieldInfo(cf.getConstPool(), "width", "I");f.setAccessFlags(AccessFlag.PUBLIC);cf.addField(f);cf.write(new DataOutputStream(new FileOutputStream("./demo/Foo.class")));}/*** ClassFile读取和写入用到的核心方法* 读取: ClassFile.read* 写入: ClassFile.write** 熟悉这两个方法, 对Java字节码结构的理解, 帮助会非常大!!!*/@Test@DisplayName("ClassFile读取和写入")public void testReadAndWriteClassFile() throws Throwable {// ClassFile读取BufferedInputStream fin = new BufferedInputStream(new FileInputStream("./demo/MyMain.class"));ClassFile cf = new ClassFile(new DataInputStream(fin));// ClassFile写入cf.write(new DataOutputStream(new FileOutputStream("./demo/MyMainCopy.class")));}}

5.相关参考

一. Javassist官方参考
    1.官网: http://www.javassist.org/
      可以下载发布包, 里面有(Jar包, 源码, 使用样例, 入门指南).
    2.入门指南: http://www.javassist.org/tutorial/tutorial.html
      就三页, 写的较为全面, 但是比较琐碎, 容易陷入细节泥潭
      建议先跑几个demo, 按照如上模型, 调试下源码 (源码比较简单), 然后再来看
    3.在线API文档: http://www.javassist.org/html/index.html
      JavaDoc文档
二. [书籍] 深入理解JVM字节码(张亚) [6.2]Javassist介绍
     围绕字节码相关的知识, 介绍都较为全面

三. [书籍] 深入理解Java虚拟机(周志明)

Javassist学习总结1相关推荐

  1. javassist学习笔记

    2019独角兽企业重金招聘Python工程师标准>>> 介绍:www.javassist.org/ javassist.ASM 对比 1.javassist是基于源码级别的API比基 ...

  2. javassist 初步学习

    javassist简介 javassist可以对一个已经编译好了的.class文件的字节码进行改动,比如说我可以为一个类添加一个方法,添加一个属性,也可以修改一个方法等,还可以对一个方法,异常进行拦截 ...

  3. 菜鸟学习笔记:Java提升篇12(Java动态性2——动态编译、javassist字节码操作)

    菜鸟学习笔记:Java提升篇12(Java动态性2--动态编译.javassist字节码操作) Java的动态编译 通过脚本引擎执行代码 Java字节码操作 JAVAssist的简单使用 常用API ...

  4. Java学习之javassist

    javassist可以实现动态编程,即动态生成class文件,或者操作class文件,下面就详细介绍. 1.读取和输出字节码 1 ClassPool pool = ClassPool.getDefau ...

  5. Java 编程的动态性,第 6 部分: 利用 Javassist 进行面向方面的更改--转载

    本系列的 第 4 部分和 第 5 部分讨论了如何用 Javassist 对二进制类进行局部更改.这次您将学习以一种更强大的方式使用该框架,从而充分利用 Javassist 对在字节码中查找所有特定方法 ...

  6. 阿里P7/P8学习路线图——技术封神之路

    一.基础篇 JVM JVM内存结构 堆.栈.方法区.直接内存.堆和栈区别 Java内存模型 内存可见性.重排序.顺序一致性.volatile.锁.final 垃圾回收 内存分配策略.垃圾收集器(G1) ...

  7. JVM插桩之四:Java动态代理机制的对比(JDK和CGLIB,Javassist,ASM)

    一.class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件, ...

  8. java类的修改三个方面_Java 编程的动态性,第 6 部分: 利用 Javassist 进行面向方面的更改--转载...

    本系列的 第 4 部分和 第 5 部分讨论了如何用 Javassist 对二进制类进行局部更改.这次您将学习以一种更强大的方式使用该框架,从而充分利用 Javassist 对在字节码中查找所有特定方法 ...

  9. proxy aspectj_使用AspectJ,Javassist和Java Proxy进行代码注入的实用介绍

    proxy aspectj 静态地或在运行时将代码片段注入已编译的类和方法中的功能可能会很有帮助. 这尤其适用于在没有源代码的第三方库中或在无法使用调试器或探查器的环境中对问题进行故障排除. 代码注入 ...

  10. 使用AspectJ,Javassist和Java Proxy进行代码注入的实用介绍

    静态地或在运行时将代码片段注入已编译的类和方法中的功能可能会很有帮助. 这尤其适用于在没有源代码的第三方库中或在无法使用调试器或探查器的环境中对问题进行故障排除. 代码注入对于处理涉及整个应用程序的问 ...

最新文章

  1. POJ3757 01分数规划
  2. 从萌新玩家到游戏开发,IEG首位女专家的升级之路
  3. 3D印花芭蕾舞鞋为舞者科学地保护双脚
  4. android崩溃拦截给出提示显示日志
  5. 上传身份证照片js_html+css+js 实现拍照预览上传图片功能
  6. 设计模式17_命令模式
  7. HDU 6188:Duizi and Shunzi(贪心)(广西邀请赛)
  8. 2019年BAT面试通关宝典:数据结构+JVM+并发编程+分布式...
  9. aspnetpager 详解
  10. 国产操作系统--NeoKylin基本操作命令汇总(一)
  11. Windows鼠标指针美化
  12. 【数字电子技术基础】数字电子钟设计
  13. Android Studio||动态改变xml图片位置+背景/旋转+平移/AnimationSet/java读取drawable图
  14. OpenCV_Viz——OpenCV中Viz的模块编译的无法解析的外部符号BUG
  15. 反向迭代器---迭代器适配器
  16. 【MQ】MQ消息中间件RabbitMQ
  17. nginx反向代理非80端口/nginx反代非80端口
  18. java byte short_Java Byte shortValue()方法
  19. 李笑来-《自学是门手艺》-笔记
  20. C# vb .NET读取识别条形码线性条码code128

热门文章

  1. 不加群提取群成员_QQ群排名优化技术教程
  2. 指数函数(复利)特性及个人发展
  3. 基于JavaWeb的小区车辆信息管理系统
  4. 开源软件与自由软件的区别
  5. 计算机键盘练习,电脑键盘指法练习经验分享
  6. IBM server guide 下载地址及列表
  7. Fedora 9 感受
  8. 【linux】linux下修改鼠标指针
  9. GRE over IPSec 主备双链路冗余配置
  10. 用 Dev-C++ 编写简单的平均数/中位数/众数/方差/一元线性回归方程计算器(附带控制台颜色设置,选择界面)