包 java.lang.instrument
代理部署为 JAR 文件。 JAR 文件清单中的属性指定将加载以启动代理的代理类。可以通过多种方式启动代理:
对于支持命令行界面的实现,可以通过在命令行上指定一个选项来启动代理。
实现可能支持在 VM 启动后的某个时间启动代理的机制。例如,一个实现可以提供一种机制,允许工具附到正在运行的应用程序,并开始将该工具的代理加载到正在运行的应用程序中。
代理可以与应用程序一起打包在可执行 JAR 文件中。
代理可以在加载时以任意方式转换类、转换模块或转换已加载类的方法的字节码。部署代理、部署将代理与应用程序打包的应用程序或使用将代理加载到正在运行的应用程序的工具的开发人员或管理员负责验证每个代理的可信度,包括代理 JAR 文件的内容和结构。
下面介绍三种启动代理的方式。
从命令行界面启动代理
如果实现提供了从命令行界面启动代理的方法,则通过向命令行添加以下选项来启动代理:
-javaagent:<jarpath>[=<options>]
在哪里<jarpath>
是代理 JAR 文件的路径,并且<options>
是代理选项。
代理 JAR 文件的清单必须在其主清单中包含属性 Premain-Class
。该属性的值是代理类.代理类必须实现一个公共静态 premain
方法,原则上类似于 main
应用程序入口点。 Java 虚拟机 (JVM) 初始化后,premain
方法将被调用,然后才是真正的应用程序 main
方法。 premain
方法必须返回才能继续启动。
premain
方法具有两种可能的签名之一。 JVM 首先尝试在代理类上调用以下方法:
public static void premain(String agentArgs, Instrumentation inst)
如果代理类未实现此方法,则 JVM 将尝试调用:
public static void premain(String agentArgs)
代理类也可能有一个 agentmain
方法,用于在 VM 启动后启动代理时使用(见下文)。使用命令行选项启动代理时,不会调用 agentmain
方法。
每个代理都通过 agentArgs
参数传递其代理选项。代理选项作为单个字符串传递,任何额外的解析都应由代理本身执行。
如果无法启动代理(例如,因为无法加载代理类,或者因为代理类没有合适的premain
方法),JVM 将中止。如果 premain
方法抛出未捕获的异常,JVM 将中止。
不需要实现来提供从命令行界面启动代理的方法。当它这样做时,它就会支持上面指定的 -javaagent
选项。 -javaagent
选项可以在同一命令行上多次使用,从而启动多个代理。 premain
方法将按照在命令行中指定代理的顺序调用。不止一个代理可以使用相同的<jarpath>
.
代理premain
方法可以做什么没有建模限制。应用程序 main
可以做的任何事情,包括创建线程,从 premain
开始都是合法的。
VM启动后启动Agent
一个实现可能会提供一种机制来在 VM 启动后的某个时间启动代理。有关如何启动的详细信息是特定于实现的,但通常应用程序已经启动并且其 main
方法已被调用。如果实现支持在 VM 启动后启动代理,则适用以下条件:
代理 JAR 的清单必须在其主要清单中包含属性
Agent-Class
。该属性的值是代理类.代理类必须实现公共静态
agentmain
方法。
agentmain
方法具有两种可能的签名之一。 JVM 首先尝试在代理类上调用以下方法:
public static void agentmain(String agentArgs, Instrumentation inst)
如果代理类未实现此方法,则 JVM 将尝试调用:
public static void agentmain(String agentArgs)
代理类也可能有一个 premain
方法,用于在使用命令行选项启动代理时使用。在 VM 启动后启动代理时,不会调用 premain
方法。
代理通过 agentArgs
参数传递其代理选项。代理选项作为单个字符串传递,任何额外的解析都应由代理本身执行。
agentmain
方法应执行启动代理所需的任何必要初始化。启动完成后,该方法应返回。如果无法启动代理(例如,因为无法加载代理类,或者因为代理类没有符合要求的agentmain
方法),JVM 不会中止。如果 agentmain
方法抛出一个未捕获的异常,它将被忽略(但可能会被 JVM 记录下来以进行故障排除)。
在可执行 JAR 文件中包含代理
JAR 文件规范为打包为 executable JAR files 的独立应用程序定义清单属性。如果实现支持将应用程序作为可执行 JAR 启动的机制,则主清单可能包含 Launcher-Agent-Class
属性以指定在调用应用程序 main
方法之前启动的代理的类名。 Java 虚拟机尝试在代理类上调用以下方法:
public static void agentmain(String agentArgs, Instrumentation inst)
如果代理类未实现此方法,则 JVM 将尝试调用:
public static void agentmain(String agentArgs)
agentArgs
参数的值始终为空字符串。
agentmain
方法应执行启动代理所需的任何必要初始化并返回。如果agent无法启动,例如agent类无法加载,agent类没有定义符合的agentmain
方法,或者agentmain
方法抛出未捕获的异常或错误,JVM会中止。
加载代理类和代理类可用的模块/类
从代理 JAR 文件加载的类由 系统类加载器 加载,并且是系统类加载器的 未命名模块 的成员。系统类加载器通常也定义包含应用程序 main
方法的类。
代理类可见的类是系统类加载器可见的类,至少包括:
引导层 中的模块导出的包中的类。引导层是否包含所有平台模块将取决于初始模块或应用程序的启动方式。
可以由系统类加载器(通常是类路径)定义为未命名模块成员的类。
代理安排由引导类加载器定义的任何类是其未命名模块的成员。
如果代理类需要链接到不在引导层中的平台(或其他)模块中的类,那么应用程序可能需要以确保这些模块在引导层中的方式启动。例如,在 JDK 实现中,--add-modules
命令行选项可用于将模块添加到根模块集中以在启动时解析。
代理安排由引导类加载器加载的支持类(通过 appendToBootstrapClassLoaderSearch
或下面指定的 Boot-Class-Path
属性)必须仅链接到定义到引导类加载器的类。不能保证所有平台类都可以由引导类加载器定义。
如果配置了自定义系统类加载器(通过 getSystemClassLoader
方法中指定的系统属性 java.system.class.loader
),则它必须定义 appendToSystemClassLoaderSearch
中指定的 appendToClassPathForInstrumentation
方法。换句话说,自定义系统类加载器必须支持将代理 JAR 文件添加到系统类加载器搜索的机制。
清单属性
为代理 JAR 文件定义了以下清单属性:
Premain-Class
- 当在 JVM 启动时指定代理时,此属性指定代理类。即,包含
premain
方法的类。当在 JVM 启动时指定代理程序时,此属性是必需的。如果该属性不存在,JVM 将中止。注意:这是一个类名,而不是文件名或路径。Agent-Class
- 如果实现支持在 VM 启动后的某个时间启动代理的机制,则此属性指定代理类。即,包含
agentmain
方法的类。如果该属性不存在,代理将不会启动,则该属性是必需的。注意:这是一个类名,而不是文件名或路径。Launcher-Agent-Class
- 如果实现支持将应用程序作为可执行 JAR 启动的机制,则主清单可能包含此属性以指定在调用应用程序
main
方法之前启动的代理的类名。Boot-Class-Path
- 引导类加载器要搜索的路径列表。路径表示目录或库(在许多平台上通常称为 JAR 或 zip 库)。在定位类的平台特定机制失败后,引导类加载器将搜索这些路径。按照列出的顺序搜索路径。列表中的路径由一个或多个空格分隔。路径采用分层 URI 的路径组件的语法。如果路径以斜杠字符 ('/') 开头,则它是绝对路径,否则它是相对的。相对路径根据代理 JAR 文件的绝对路径进行解析。格式错误和不存在的路径将被忽略。当代理在 VM 启动后的某个时间启动时,不代表 JAR 文件的路径将被忽略。该属性是可选的。
Can-Redefine-Classes
- boolean(
true
或false
,大小写无关)。是否能够重新定义此代理所需的类。true
以外的值被视为false
。该属性是可选的,默认为false
。Can-Retransform-Classes
- boolean(
true
或false
,大小写无关)。是重新转换此代理所需的类的能力。true
以外的值被视为false
。该属性是可选的,默认为false
。Can-Set-Native-Method-Prefix
- boolean(
true
或false
,大小写无关)。是否能够设置此代理所需的本机方法前缀。true
以外的值被视为false
。该属性是可选的,默认为false
。
代理 JAR 文件可能同时具有清单中的 Premain-Class
和 Agent-Class
属性。当使用 -javaagent
选项在命令行上启动代理时, Premain-Class
属性指定代理类的名称,而 Agent-Class
属性将被忽略。同样,如果代理在 VM 启动后的某个时间启动,则 Agent-Class
属性指定代理类的名称(忽略 Premain-Class
属性的值)。
检测模块中的代码
作为在引导类加载器的搜索路径或加载主代理类的类加载器的搜索路径上部署支持类的代理的辅助,Java虚拟机安排转换类的模块读取未命名模块两个类加载器。
- 自从:
- 1.5
-
类描述此类用作
Instrumentation.redefineClasses
方法的参数块。类文件的转换器。当其输入参数无效时由ClassFileTransformer.transform
的实现抛出。此类提供检测 Java 编程语言代码所需的服务。当无法修改指定的类之一时,由Instrumentation.redefineClasses
的实现抛出。抛出以指示无法修改模块。