Java代理Agents通过提供使我们能够侵入JVM中正在运行的Java程序的服务,在最底层工作。Java的这一强大但不可思议的部分具有在错误操作时使JVM崩溃的能力。本文简要介绍了这个概念,并介绍了它的工作原理。
表示Java Agents的类显然只不过是Java API库中的任何其他类。但是,让它们与众不同的是,它们遵循某种约定,这种约定使Java代码能够拦截JVM中运行的另一个应用程序。其目的只是让代理调查或修改字节码。这是一个强大但不可思议的特性,超出了Java程序通常的功能范围。在某种程度上,它可以闯入一个程序,修改字节码或造成混乱。请理解,这不是添加到Java中的新技术或功能。自JDK1.5以来,它一直是库的一部分。这意味着使用它们也有一些真正的好处。但是,在讨论它们的优点以及如何使用它们之前,让我们先看看在Java中哪里可以找到它们。
Java Agents 和 Instrumentation
Java agent是Java Instrumentation API的一部分。检测API提供了一种修改方法字节码的机制。这可以静态和动态地完成。这意味着我们可以通过向程序中添加代码来更改程序,而不必涉及程序的实际源代码。结果可能会对应用程序的整体行为产生重大影响。
Java agent和instrumentation API位于名为Java.lang.intrumentation
的包中。
Java agent的使用
Java agent可以有多种用途,如面向方面编程(AOP)、变异测试、评测等。AOP通常会在不更改代码的情况下向现有程序添加日志记录或安全性等行为。它使用Java代理来操作字节码,并将其功能与程序结合起来。监视JVM级别的参数,如对象创建、垃圾收集、线程执行等,是探查器的工作。评测工具显著地使用Java代理评测执行中程序的JVM参数。
还有许多其他情况下,Java agent和instrumentation API非常方便。
如何编写Java代理
实现Java agent的类必须实现一个名为
public static void premain(String agentArgs, Instrumentation inst)
此方法构成代理的入口点,就像常规Java程序的入口点是主方法一样。
JVM初始化后,调用premain
方法;这表示代理。可以有几个这样的代理;因此,将根据JVM初始化期间指定的代理的顺序调用每个premain
方法。如果找不到特定的premain
方法,JVM会依次调用premain
方法的重载版本,例如
public static void premain(String agentArgs)
代理类还可能包含JVM在agent启动后通常使用的方法,例如
public static void agentmain(String agentArgs,
instrumentation inst)
或者,它的重载版本
public static void agentmain(String agentArgs)
这是JVM的典型例程,一旦该例程完成,就会调用main
方法。
另一件重要的事情是,Java代理在开发期间必须在资源目录的META-INF
文件夹中包含MANIFEST.MF
文件。此文件包含有关包分发的元数据信息。此文件作为其JAR打包的一部分包含。MANIFEST.MF
文件中包含的属性提供了有关为什么需要这样做的线索。这些属性如下所示:
- Premain-class:此属性定义代理类。如果未定义此属性,JVM将中止。
- Agent-class:它定义了在JVM启动后启动Java代理的机制。如果此属性未定义,代理将不会启动。
- Can-Redefine-Classes:这定义了代理重新定义类的能力。该值可以是true或false。
- Can-Retransform-Classes:这定义了代理重新传输类的能力。该值可以是true或false。
- Can-Set-Native-Method-Prefix:这定义了代理设置本机方法前缀的能力。该值可以是true或false。
- Boot-Class-Path:定义引导类加载程序的搜索路径列表。
一个简单的例子
探查器工具通常通过从JVM提取信息来报告运行时Java对象的不同参数。这些参数包括关于使用检测框架的对象的内存使用等信息。
1. 这里我们使用premain
方法创建一个代理类。
2. 传递给premain
方法的检测实例将提供有关对象大小的信息。
3. 将代理类与MANIFEST.MF
文件一起打包到JAR文件中。
4. 使用命令行参数将代理传递给JVM。
这是我们将在示例中使用的示例类。这没什么特别的。
package com.mano.examples;
public class Main {
public static void greet(String msg){
System.out.println(msg);
}
public static void main(String[] args){
greet("Hello Agents");
}
}
代理类
带有premain
方法的instrumentation agent类用于检索我们需要的信息。插装接口的实现被传递给premain
方法。我们使用由instrumentation接口定义的getObjectSize
方法来获取运行时主对象的内存使用情况。
package com.mano.examples;
import java.lang.instrument.Instrumentation;
public class MyAgentClass {
public static void premain(String agentArgs,
Instrumentation inst) {
System.out.println(inst.getObjectSize
(new Main()))
}
}
之后,我们必须创建MANIFEST.MF
文件。这只是一个文本文件,我们在其中放置与代理类相关的信息。JVM将使用它来加载代理。该文件通常存储在META-INF
目录中。我们的示例所需的内容非常基本:
Manifest-Version: 1.0
Premain-Class: com.mano.examples.MyAgentClass
现在,编译所有Java文件以创建类文件。最后,创建JAR文件,如下所示:
jar -cmf META-INF/MANIFEST.MF myagent.jar com/mano/examples/
MyAgentClass.class
部署Java Agents
创建代理后,它将作为JAR文件部署。清单文件中的属性指定将加载以启动代理的代理类。请注意,启动代理有很多方法:使用命令行、运行时或作为JAR可执行文件。我们将在这里使用命令行。
使用命令行运行Agents代理
命令行是:
java -javaagent:myagent.jar -cp . com.mano.examples.Main
这表示premain
方法将在应用程序执行之前运行,并创建Main
实例的大小。
结论
仪器API所提供的功能可以进行多种创新。AOP是一个简单的例子。虽然Java代理和Java Instrumentation API在应用程序开发中不经常使用,但是关于它的全部内容的想法可以澄清Java的许多其他方面。这里给出的代码示例是初步的,只是为了说明如何创建代理。
原文地址:https://www.developer.com/design/what-is-java-agent/
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/2353.html
暂无评论