前言
字节码我们都知道是java
文件经过编译之后的class
文件,每一个字节码文件都要由10部分按照固定的顺序组成;增强其实就是对字节码文件进行改造生成一个新的文件,已达到我们的目的,比如动态代理,AOP
等;当然增强完需要能被使用,所以涉及到到加载的问题;在介绍之前我们先来看看都有哪些字节码增强技术。
常见技术
常见的字节码增强技术大致分为两类:静态增强和动态增强;静态增强最常见的就是AspectJ
了,可以直接编译类,有自己的语法;动态增强包括:ASM
、Javassist
、Cglib
、Java Proxy
;下面分别做简单介绍。
AspectJ
AspectJ
来自于Eclipse
基金会,属于静态织入,主要采用的是编译期织入,在这个期间使用AspectJ
的acj
编译器(类似javac
)把aspect
类编译成class
字节码;下面看一下AspectJ
是如何使用的;
-
下载安装
AspectJ
官网地址:https://www.eclipse.org/aspectj/
直接下载最新版:AspectJ 1.9.6
;直接运行以下命令即可安装:java -jar aspectj-1.9.6.jar
指定安装目录,然后配置
classPath
和path
即可:ASPECTJ_HOME=E:\aspectj1.9 CLASSPATH=...%ASPECTJ_HOME%\lib\aspectjrt.jar PATH=...%ASPECTJ_HOME%\bin
-
编译使用
可以直接使用AspectJ
提供的Demo
进行测试examples\tjp
目录下:
里面有两个java
文件分别是Demo.java
和GetInfo.java
,Demo
就是我们正常的java
文件,而GetInfo
是增强文件,里面有一些AspectJ
语法,需要使用ajc
命令编译E:\aspectj1.9\doc\examples\tjp>ajc -argfile files.lst E:\aspectj1.9\doc\examples\tjp>cd .. E:\aspectj1.9\doc\examples>java tjp.Demo Intercepted message: foo in class: tjp.Demo Arguments: 0. i : int = 1 1. o : java.lang.Object = tjp.Demo@6e3c1e69 Running original method: Demo.foo(1, tjp.Demo@6e3c1e69) result: null Intercepted message: bar in class: tjp.Demo Arguments: 0. j : java.lang.Integer = 3 Running original method: Demo.bar(3) result: Demo.bar(3) Demo.bar(3)
可以发现经过ajc编译后的新class文件,go和bar两个方法都得到了增强,在方法调用前和后、以及方法参数都添加了日志输出;可以发现
AspectJ
在运行前就已经对class文件做了增强处理;Spring AOP借鉴了AspectJ
的一些概念,但是在实现上并没有采用AspectJ
而使用动态增强技术;
ASM
ASM
是一个通用的Java
字节码操作和分析框架,它可以用来修改现有的类或直接以二进制形式动态生成类;ASM
提供了一些常见的字节码转换和分析算法,从中可以构建定制的复杂转换和代码分析工具;几个核心的类:
- ClassVisitor:用于生成和转换编译类的
ASM API
基于ClassVisitor
抽象类,这个类中的每个方法都对应于同名的类文件结构; - ClassReader:此类主要功能就是读取字节码文件,然后把读取的数据通知
ClassVisitor
; - ClassWriter:其继承于
ClassVisitor
,主要用来生成类;
ASM偏底层字节码操作,所有需要对字节码命令比较熟悉,但是性能高;比如FastJson、Cglib、Lombok等都依赖ASM;大体的操作步骤就是首先需要加载原Class文件,然后通过访问者模式访问所有元素,在访问的过程中对各元素进行改造,最后重新生成一个字节码的二进制文件,根据需求进行加载新Class或者重加载;
更多参考:ASM入门篇
Javassist
正因为ASM
需要对字节码命令熟悉,而字节码本身就比较晦涩难懂,所有就有了更容易理解的增强工具Javassist
,可以直接使用Java编码的方式,无需了解相关字节码指令,对开发者更加友好,当然性能肯定不及ASM
,下面看一下几个常见的类:
- ClassPool:可以简单理解就是存放类的池子,所有
CtClass
都要从池中获取; - CtClass:表示一个类文件,可以通过一个类的全限定名来获取一个
CtClass
对; - CtMethod:对应的类中的方法,可以通过
CtClass
获取指定方法; - CtField:对应类中的属性,可以通过
Ct