场景:你需要在Java进程中获取所有已知加载类的字节码。或者你想要调试运行时发生的某种类型的instrumentation。

看这篇文章前你需要了解:Java字节码

下面介绍如何获取加载到JVM中的所有类的字节码。

javaagent

通常,我们可以把javaagent当成一个JVM插件。一种专门的jar文件,它可以利用JVM提供的InstrumentationAPI。Java1.5提供了InstrumentationAPI。

java_agent_overview_min

成功创建一个javaagent需要三个环节:

代理类

代理类必须有premain方法,当Java虚拟机启动时,在执行main函数之前,JVM会先运行-javaagent所指定jar包内Premain-Class这个类的premain方法,其中,该方法可以签名如下:

  1. publicstaticvoidpremain(StringagentArgs,Instrumentationinst)
  2. publicstaticvoidpremain(StringagentArgs)

JVM会优先加载1签名的方法,加载成功忽略2,如果1没有,加载2方法。这个逻辑在sun.instrument.InstrumentationImpl类中。

一些元信息(告诉JVM为我们的代理类提供哪些功能)

定义一个MANIFEST.MF文件,必须包含Premain-Class选项,且指定我们的代理类,通常也会加入Can-Redefine-ClassesCan-Retransform-Classes选项。

将代理类和MANIFEST.MF文件打成jar包。

一种使JVM加载jar文件和代理的方式

使用参数-javaagent:agent.jar=[agentArgs]启动要代理类中的premain方法。
例如:

1
java-javaagent:sample-agent.jar=hello-jarsample-release.jar

agentmain

  1. 定义一个MANIFEST.MF文件,文件中必须包含Agent-Class
  2. 创建一个Agent-Class指定的类,该类必须包含agentmain方法(参数和premian相同)
  3. 将MANIFEST.MF和Agent类打成jar包
  4. 将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