字节码

  1. javaagent

1)简述
从java5开始,jdk中新增了一个java.lang.instrument.Instrumentation 类,它提供在运行时重新加载某个类的的class文件的api。
通过addTransformer可以加入一个转换器,转换器可以实现对类加载的事件进行拦截并返回转换后新的字节码,通过redefineClasses或retransformClasses都可以触发类的重新加载事件。

2)加载方式
在jvm启动时指定agent,Instrumentation对象会通过agent的premain方法传递。
在jvm启动后通过jvm提供的机制加载agent,Instrumentation对象会通过agent的agentmain方法传递。

3)主流的作用
java instrument在很多应用领域都发挥着重要的作用,比如:

apm:(Application Performance Management)应用性能管理。pinpoint、cat、skywalking等都基于Instrumentation实现
idea的HotSwap、Jrebel等热部署工具
应用级故障演练
Java诊断工具Arthas、Btrace等

二, 常用字节码框架

2.1 ASM

2.1.1 ASM概述

ASM 是一个 Java 字节码操控框架。

它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入Java 虚拟机之前动态改变类行为。

ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。

2.2.1 ASM核心模块
  • ClassReader:负责解析 .class 文件中的字节码,并将所有字节码传递给 ClassWriter。
  • ClassVisitor:负责访问 .class 文件的各个元素,可以解析或者修改 .class 文件的内容。
  • ClassWriter:继承自ClassVisitor,它是生成字节码的工具类,负责将修改后的字节码输出为 byte 数组。

ASM依赖

1
2
3
4
5
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.2</version>
</dependency>

2.2 CGLib

CGLib,全称是:Code Generation Library。

CGLib 基于 asm,将代理对象类的 class 文件加载进来,通过修改其字节码动态生成子类,在子类中采用方法拦截的技术拦截所有父类方法的调用并顺势织入横切逻辑。

最底层的是字节码ByteCode,字节码是 Java 为了保证“一次编译,到处运行”而产生的一种虚拟的指令格式。
位于字节码之上的是ASM,这是一种直接操作字节码的框架,应用 ASM 需要对 Java字节码、Class结构比较熟悉。
位于ASM之上的是CGLIB、Groovy、BeanShell,后两种并不是 Java 体系中的内容而是脚本语言,它们通过 ASM 框架生成字节码变相执行Java代码,这说明在JVM中执行程序并不一定非要写Java代码,只要你能生成Java字节码,JVM并不关心字节码的来源,当然通过Java代码生成的JVM字节码是通过编译器直接生成的,算是最“正统”的 JVM 字节码。
位于CGLIB、Groovy、BeanShell之上的就是Hibernate、Spring AOP这些框架了。
最上层的是Applications,即具体应用,一般都是一个Web项目或者本地跑一个程序。
FastClass机制
CGLib动态代理执行代理方法效率之所以比JDK高,是因为Cglib采用了FastClass机制,它为代理类和被代理类各生成了一个类,这个类会为代理类与被代理类的方法分类index。

这个index作为方法参数,FastClass可以直接定位到要调用的方法进行调用,这样省去了反射调用,所以效率比JDK动态代理快。

FastClass不是与代理类一起生成的,而是在第一次执行MethodProxy invoke/invokeSuper时生成的并放入缓存。