简介
几个对比: https://segmentfault.com/a/1190000009956534
ASM(Automated Storage Management)
javassist
动态代理
cglib(Code Generatator Library)
BCEL(Byte Code Engineering Library)
instrument
jdt-AST
类加载,探针,ASM,动态代理,instrument,agent
JavaAgent 是一种可以动态修改java字节码的技术,其实现原理
内定的方法名是premain
premain: 启动时,配置javaagent参数来启动
main
agentmain: attach方式,在运行过程中动态地设置加载代理类
public static void premian(String agentOps,Instrumentation inst){
//在JVM启动时,初始化函数loadClassAndCallPremain方法执行Premain-Class类置顶的premain方法
inst -> 传入代理实例,操作字节码文件 类加载
}
public static void premian(String agentOps){
}
public static void agetmain(){
//JVM启动后,通过VirtualMachine附着 一个Instrument,如vm.loadAgent(jar),会调用sun.instrument.instrumentationImpl实现类的loadClassAndCallAgentmain方法执行Agentmain-Class指定类的agentmain方法
}
## Instrument premain、agentmain方法中两个参数agentArgs、inst代表什么,
1. agentArgs:代理程序命令行中输入参数,同“-javaagnet”一起传入,与main函数不同的是,这个参数是一个字符串而不是一个字符串数组
2. inst: java.lang.instrumentation实例,由JVM自动传入,集中了几乎所有功能方法,如:类操作,classpath操作等
javaAgent与Java字节码注入技术的Java探针工具
javaAgent实现原理
-
JVMTI
JVMTI,是JVM暴露出来给用户扩展使用的接口集合,JVMTI是基于事件驱动JVM级别的AOP java1.6
JVMTI可以支持第三方工具程序以代理的方式连接和访问JVM,并利用JVMTI提供的丰富编程接口,完成JVM相关功能 -
JVMTIAgent
JVMTIAgent是一个动态库,利用JVMTI暴露出来的接口实现用户自行的逻辑(idea的调试也是通过这个实现的)
JVMTIAgent主要有三个方法:
- Agent_OnLoad方法,agent在启动时加载,就执行这个方法
- Agent_OnAttach方法,agent不是在启动时候加载的,我们先attach到目标线程上,然后对于的目标进程load命令来加载agent
- Agent_OnUnload方法,agent卸载时调用
- java.lang.instrument
java.lang.instrument中需要关注的是ClassFileTransformer和Instrumentation接口。
public interface ClassFileTransform{
byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException
}
//如果transform方法返回null, 表示我们不对类进行处理直接返回。否则,会用我们返回的byte[]来代替原来的类。
//也不会生成新的类,也不需要原类的接口
Instrumentation接口。ClassFileTransformer必须添加进Instrumentation才能生效。
Instrumentation inst;
ClassFileTransformer classFileTransformer;
inst.addTransformer(classFileTransformer);
META-INF/MANIFEST.MF参数清单
- instrument agent
instrument agent实现了Agent_OnLoad方法和Agent_OnAttach方法
-
JVM attach机制
jvm attach机制上JVM提供了一种jvm进程间通信的功能,能让一个进程传命令给另一个进程- 比如进行线程dump(程序运行期间,dump指令运行的底层原理,守护进程监听?-jstack -pid等参数传给dump的线程来执行)
-
ClassTransform
加载类文件的时候发出ClassFileLoad事件,交给Instrument agent来调用 java agent里注册的ClassFileTransformer实现字节码的修改 -
Class Redefine
参考链接:https://www.cnblog.com/jackion5/p/10680343/html
最直接改造java类的方法莫过于直接改写class文件
字节码增强技术框架
ASM是一个字节码操作框架: Automated Storage Management,需要对class字节码熟悉
javaassist对字节码修改
byte buddy
ASM
ASM是一个字节码操作框架
它能被用来动态生成类,或者增强既有类的功能
ASM可以直接产生二进制class文件,也可以在类被载入Java虚拟机之前动态改变类的行为
与 BCEL 和 SERL不同, ASM提供了更为现代的模型
类转换的负载小
生成的代码可以直接覆盖原来的类,或者是原始类的子类
案例:lambda表达式,cglib动态代理类
没有反射带来的性能开销
Core(各种Visitor):提供了ClassReader 和 ClassWriter
tree:
analysis:提供了一个静态字节码分析框架,除了树包之外,还可以使用它来实现真正复杂的类转化,这些转换需要知道每条指令的堆栈映射的状态
ASM字节码操纵的两种方式:
- CoreApi 访问者模式(Visitor):基于事件驱动
- TreeAPI 树节点模式:基于面向对象
AOP实现:
AdviceAdapter是MethodVisitor的子类,使用AdviceAdapter可以更方便的修改方法的字节码
Opcodes: JVM操作码
LocalVariablesSorter:对方法的参数&本地局部变量进行重新编号
GeneratorAdapter:封装了原始字节码操作,例如调用方法时的所有visitMethod封装为各种InvokeXXX
- OnMethodEnter
- OnMethodExit
ASM辅助工具
ASM Bytecode Viewer
Java Instrument
在整个虚拟上挂一个钩子程序,每次装入一个新类的时候,都必须执行一遍这段程序,即使这个类不要改变
更适用于监控和控制虚拟机的行为
但是agentmain,可以动态改变已载入的文件,在程序运行期间做些操作
Attach APi 后台监听进程开启attach listener(如果未开启,由另外一个线程监听操作)
动态代理 Proxy类
接口 -> 实现类 -> 前后之类增加一些额外的操作
静态代理和动态代理的区别:
1. 代理类和实现类实现了相同的接口,方法增加,代理类的方法也需要相应增加
2. 动态代理运用反射机制动态创建而成-> 不用为每一个方法创建代理实现类
3. 都需要传入被代理对象
Proxy类,只面向接口,方法
反射
java.lang.reflect.InvocationHandler接口
java.lang.reflect.Proxy
interface InvocationHadler(){
invoke(...){
}
}
反射引入性能代价
面向接口编程
只能改写method
public class dongtaidaili implement InvocationHandler{
targer = 被代理对象
//只能代理接口,因为newProxyInstance方法只接受接口方法作为参数,最后调用newInstance
Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this)
invoke(Object proxy,Method method,Object[] args){ }
}
Cglib
参考链接:https://blog.csdn.net/mulinsen77/article/details/86565891
利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理
动态代理和cglib的区别:
- 代理对象是目标对象的子类,主要是对指定的类生成一个子类,覆盖其中的方法
- 拦截器必须实现MethodInterceptor接口
- hibernate中的session.load采用的是cglib实现的
- Spring如何选择用jdk还是cglib:
当bean实现接口时,sprign就会用jdk的动态代理
当bean没有实现接口时,Spring会使用cglib实现
可以强制使用cglib
https://www.cnblogs.com/clds/p/4985893.html
javasist
类依赖分析器
jdeps - java dependencies 、java8开始拥有
jdeps 命令显示java类文件的包级或类级依赖关系,输入可以是.class文件、目录、jar文件路径名
Android常见的依赖分析方案
获得模块与类的关系、类和类之间方法级
JDT - AST
https://blog.mythsman.com/post/5d2c11c767f841464434a3bf
https://segmentfault.com/a/1190000000609246
https://blog.csdn.net/lovelion/article/details/18953869 AST树描述比较好,完整抽象语法树
https://www.jianshu.com/p/68027eaf45ad AST NODE 描述
AST 节点结构 node类型
ASTNode
ASTVisitor
Messager主要是用来在编译期打log用的
JavacTrees提供了待处理的抽象语法树
TreeMaker封装了创建AST节点的一些方法
Names提供了创建标识符的方法
网友评论