场景:你需要在Java进程中获取所有已知加载类的字节码。或者你想要调试运行时发生的某种类型的instrumentation。
看这篇文章前你需要了解:Java字节码。
下面介绍如何获取加载到JVM中的所有类的字节码。
javaagent
通常,我们可以把javaagent当成一个JVM插件。一种专门的jar文件,它可以利用JVM提供的InstrumentationAPI。Java1.5提供了InstrumentationAPI。
成功创建一个javaagent需要三个环节:
代理类
代理类必须有premain
方法,当Java虚拟机启动时,在执行main函数之前,JVM会先运行-javaagent
所指定jar包内Premain-Class
这个类的premain
方法,其中,该方法可以签名如下:
publicstaticvoidpremain(StringagentArgs,Instrumentationinst)
publicstaticvoidpremain(StringagentArgs)
JVM会优先加载1签名的方法,加载成功忽略2,如果1没有,加载2方法。这个逻辑在sun.instrument.InstrumentationImpl
类中。
一些元信息(告诉JVM为我们的代理类提供哪些功能)
定义一个MANIFEST.MF
文件,必须包含Premain-Class
选项,且指定我们的代理类,通常也会加入Can-Redefine-Classes
和Can-Retransform-Classes
选项。
将代理类和MANIFEST.MF
文件打成jar包。
一种使JVM加载jar文件和代理的方式
使用参数-javaagent:agent.jar=[agentArgs]
启动要代理类中的premain
方法。
例如:
1 | java-javaagent:sample-agent.jar=hello-jarsample-release.jar |
agentmain
- 定义一个MANIFEST.MF文件,文件中必须包含Agent-Class
- 创建一个Agent-Class指定的类,该类必须包含agentmain方法(参数和premian相同)
- 将MANIFEST.MF和Agent类打成jar包
- 将jar包载入目标虚拟机。目标虚拟机将会自动执行agentmain方法执行方法逻辑,同时,ClassFileTransformer也会长期有效,在每一个类加载器加载Class的时候都会拦截
相关文章:
https://www.cnblogs.com/stateis0/p/9062199.html
https://www.cnblogs.com/stateis0/p/9062201.html
https://www.jrebel.com/blog/java-bytecode-tutorial
https://blogs.oracle.com/ouchina/javaagent