更新时间:2024-06-07 07:16:38作者:佚名
0 简介
使用java.lang.进行动态操作是Java SE 5的新特性,它将Java函数从本地代码中解放出来,让Java代码能够自己解决问题。有了它,开发人员可以构建独立于应用程序的代理,来监控和协助运行在JVM上的程序,甚至可以替换和修改某些类的定义。有了该功能,开发人员可以实现更加灵活的运行时虚拟机监控和Java类操作。该功能实际上提供了一种虚拟机级别支持的AOP实现方法,开发人员可以在不升级或更改JDK的情况下实现某些AOP功能。
在Java SE 6中,包被赋予了更强大的功能:后启动、本地代码、动态变化等等,这些改变意味着Java拥有了更强的动态控制和解释能力,使得Java语言变得更加灵活多变。
在Java SE6中,最大的改变使得运行时成为可能。在Java SE 5中,需要在运行前使用命令行参数或者系统参数设置代理类。在实际运行中,在虚拟机初始化的时候(大多数Java类库加载之前),就会启动代理类的设置,并在虚拟机中设置一个回调函数来检测具体类的加载情况并完成实际的工作。但在很多实际情况下,我们没有办法在虚拟机启动的时候就为其设置代理,这其实限制了应用。Java SE 6的新特性改变了这种情况,通过Java Tool API中的方法,我们可以很方便地在运行过程中动态地设置加载代理类,达到目的。
此外,接口访问也是Java SE 6的一个全新特性,它使得以前不可能实现的功能——接口访问可以在Java SE 6中通过一个或者一系列的添加来完成。
最后,Java SE 6 中添加了动态添加类路径的功能。所有这些新特性使得包的功能更加强大,从而使 Java 语言本身更加强大。
1 基本功能及使用方法
JVMTI(Java Tool)是Java虚拟机为JVM相关工具提供的一套原生编程接口。JVMTI在Java SE 5中被引入,整合并取代了之前使用的Java(JVMPI)和Java Debug(JVMDI)。在Java SE 6中instrument是什么意思,JVMPI和JVMDI已经消失。JVMTI提供了一套“代理”程序机制,可以支持第三方工具程序以代理的方式连接并访问JVM,并利用JVMTI提供的丰富编程接口完成很多JVM相关的功能。
其实java.lang.包的实现就是基于这种机制的:在实现中有一个JVMTI代理,通过调用JVMTI中Java类相关的函数来完成Java类的动态操作。除了上述函数之外,JVMTI还在虚拟机内存管理、线程控制、方法和变量操作等方面提供了大量有价值的函数。
1.1 VM 预启动设置
最大的作用就是可以动态的改变和操作类定义,在Java SE 5以及后续版本中,开发者可以在普通的Java程序(有main函数的Java类)运行时,通过--参数指定具体的jar文件(包含代理)来启动代理程序。
功能比较强大,可以批量转换很多类别。
1.2 VM启动后动态
在Java SE 5中,开发者只能发挥自己的想象力,所能做的事情仅限于在main函数执行之前,这种方式有一定的局限性。
Java SE 6 在Java SE 5的基础上对此情况进行了改进,开发者可以在main函数开始执行后,启动自己的程序。
在 Java SE 6 中,有一种名为“手拉手运行”的方法,可以在主函数开始运行后运行。就像函数一样,开发人员可以编写一个包含“”函数的 Java 类:
类似地,[1] 的优先级高于 [2],将首先执行。与函数一样,开发人员可以对类进行各种操作。和 Inst 的用法相同。
与“-Class”类似,开发人员必须在文件中设置“Agent-Class”来指定包含该功能的类。
但不同的是,它需要在main函数开始运行之后才启动,这样的时机该如何确定,又该如何实现这样的功能呢?
在 Java SE 6 的文档中,开发者可能无法在与 java.lang. 包相关的文档部分看到清晰的介绍,更不用说具体的应用示例了。然而,在 Java SE 6 的众多新特性中,却有一个不显眼的地方透露了用法。这就是 Java SE 6 中提供的 API。
该API并非标准的Java API,而是Sun提供的用于将代理工具程序“附加”到目标JVM的扩展API,通过它,开发人员可以方便地监控JVM并运行附加的代理程序。
1.3 本地方法
在JDK 1.5版本中,没有办法处理Java本机方法()留学之路,而在Java标准JVMTI下也没有办法改变它,这使替换本机方法变得非常困难。一个更直接简单的想法是在启动时替换本机代码所在的动态链接库——但这本质上是静态替换,而不是动态替换。而且,这可能需要编译大量的动态链接库——比如我们有三个本机函数,假设每一个都需要替换,而不同的应用程序可能需要不同的组合。如果我们将三个函数都编译在同一个动态链接库中,则将需要多达8个不同的动态链接库才能满足需求。当然instrument是什么意思,我们也可以独立编译它们,这也需要6个动态链接库——无论如何,这种繁琐的方法是不可接受的。
在Java SE 6中,为了解决一些问题,提出了一种新的代码解析方式作为原有解析方式的补充。也就是说,在新版本的java.lang.包中,我们多了一种代码方法——。
假设我们有一个名为 的函数,在执行过程中,需要将其指向另外一个函数(需要注意的是,在目前的标准 JVMTI 下,除了函数名不同,其他都需要保持一致)。例如我们的 Java 代码为:
是不是很有趣呢?所以如果我们要做类似的工作,一个好的建议是先用Java写一个接口,用javah工具生成一个c文件,看看它实际解析的函数名是什么,这样就可以避免一些不必要的麻烦。
另一个事实是,与我们想象的不一样,对于两个或更多个,虚拟机不会进行更多解析;它不会尝试删除一个,然后组装函数接口。它会且只进行两次解析。
总之,新的方式改变了Java中代码不能动态改变的弊端。目前使用JNI编写代码也是Java应用中很重要的一个部分,所以它的动态性就意味着整个Java都可以动态改变——现在我们的代码可以使用plus来动态改变函数指针了。如上所说,如果找不到,虚拟机就会尝试做标准分析,这就给我们提供了一种动态替换代码的方法。我们可以把很多不同的函数编译成一个动态链接库,通过封装的功能,让函数像Java函数一样动态地改变和替换。当然现在还是有一些限制的,比如不同的会各有各的,也就是各自负责他替换的所有类,而不是某个具体的类——所以这个粒度可能不够精确。
1.4 / 动态添加
我们知道,通过设置系统参数或者虚拟机启动参数,我们可以设置虚拟机运行时启动时的一个类加载路径(-)和类加载路径(-cp),当然运行后我们无法替换它。但是,有时候我们需要加载一些jar包进去,就不能应用上面两种方法了;或者需要在虚拟机启动后再加载一些jar包进去。在Java SE 6中,我们可以这样做。
要实现这几点其实很简单,首先我们还是需要确认虚拟机已经支持这个功能,然后添加所需的/,我们可以在 our 中使用 / 来完成这个任务。
同时我们可以注意到,在代理中添加Boot-Class-Path,其实可以在动态加载代理的同时,添加自己的引导类路径。当然,在Java代码中可以更动态、更方便、更智能地完成——我们可以很方便地添加判断和选择组件。
这里我们还需要注意几点:
首先,我们添加的jar文件中不应该包含任何与系统相关的同名类,否则,一切都将变得不可预测——这不是一个工程师想要的结果,对吧?
其次我们要注意虚拟机的工作方式,它会把解析的结果记录下来,比如我们曾经要求读取某个类,但是失败了,它就会记住这件事,即使我们后面动态添加了一个包含这个类的jar,它还是会认为我们无法解析这个类,会报上次一样的错误。
再次,我们知道Java语言中有一个系统参数“java.class.path”,这个参数记录了我们当前的,但是虽然我们用这两个函数来实际改变实际的,但是对这个本身不会产生任何影响。
在 包中我们可以发现一个很有意思的东西,Sun 的设计者告诉我们这个函数其实依赖于一个方法——这是一个非 函数,所以我们不建议直接使用它(使用反射等)。其实包中的两个函数已经可以很好的解决我们的问题了。
1.5 META-INF/.MF 列表
以下是代理 jar 文件的列表:
推荐公众账号: