Java编程的动态性,第5部分: 动态转换类

在经过一段时间的休息之后,Dennis Sosnoski 又回来推出了他的 Java 编程的动态性系 列的第 5 部分。您已在前面的文章中看到了如何编写用于转换 Java 类文件以改变代码行为 的程序。在本期中,Dennis将展示如何使用 Javassist 框架,把转换与实际的类加载过程结 合起来,用以进行灵活的“即时”面向方面的特性处理。这种方法允许您决定想要在运行时 改变的内容,并潜地在每次运行程序时做出不同的修改。在整个过程中,您还将更深入地了 解向JVM 中加载类的一般问题。

在第 4 部分“ 用 Javassist 进行类转换”中,您学习了如何使用 Javassist 框架来转 换编译器生成的 Java 类文件,同时写回修改过的类文件。这种类文件转换步骤对于做出持 久变更是很理想的,但是如果想要在每次执行应用程序时做出不同的变更,这种方法就不一 定很方便。对于这种暂时的变更,采用在您实际启动应用程序时起作用的方法要好得多。

JVM 体系结构为我们提供了这样做的便利途径――通过使用 classloader 实现。通过使 用 classloader 挂钩(hook),您可以拦截将类加载到 JVM 中的过程,并在实际加载这些 类之前转换它们。为了说明这个过程是如何工作的,我将首先展示类加载过程的直接拦截, 然后展示 Javassist 如何提供了一种可在您的应用程序中使用的便利捷径。在整个过程中, 我将利用取自本系列以前文章中的代码片断。

加载区域

运行 Java 应用程序的通常方式是作为参数向 JVM 指定主类。这对于标准操作没有什么 问题,但是它没有提供及时拦截类加载过程的任何途径,而这种拦截对大多数程序来说是很 有用的。正如我在第 1 部分“ 类和类装入”中所讨论的,许多类甚至在主类还没有开始执 行之前就已经加载了。要拦截这些类的加载,您需要在程序的执行过程中进行某种程度的重 定向。

幸运的是,模拟 JVM 在运行应用程序的主类时所做的工作是相当容易的。您所需做的就 是使用反射(这是在不得 第 2 部分 中介绍的)来首先找到指定类中的静态 main() 方法, 然后使用预期的命令行参数来调用它。清单 1 提供了完成这个任务的示例代码(为简单起见 ,我省略了导入和异常处理语句):

清单 1. Java 应用程序运行器

public class Run
{
   public static void main(String[] args) {
     if (args.length >= 1) {
       try {

         // load the target class to be run
         Class clas = Run.class.getClassLoader().
           loadClass(args[0]);

         // invoke "main" method of target class
         Class[] ptypes =
           new Class[] { args.getClass() };
         Method main =
           clas.getDeclaredMethod("main", ptypes);
         String[] pargs = new String[args.length-1];
         System.arraycopy(args, 1, pargs, 0, pargs.length);
         main.invoke(null, new Object[] { pargs });

       } catch ...
       }

     } else {
       System.out.println
         ("Usage: Run main-class args...");
     }
   }
}

要使用这个类来运行 Java 应用程序,只需将它指定为 java 命令的目标类,后面跟着应 用程序的主类和想要传递给应用程序的其他任何参数。换句话说,如果用于运行 Java 应用 程序的命令为:

java test.Test arg1 arg2 arg3

您相应地要通过如下命令使用 Run 类来运行应用程序:

java Run test.Test arg1 arg2 arg3

时间: 2024-08-31 02:01:11

Java编程的动态性,第5部分: 动态转换类的相关文章

Java编程的动态性,第1部分: 类和类装入

本文是这个新系列文章的第一篇,该系列文章将讨论我称之为 Java 编程的动态性的一系 列主题.这些主题的范围从 Java 二进制类文件格式的基本结构,以及使用反射进行运行时 元数据访问,一直到在运行时修改和构造新类.贯穿整篇文章的公共线索是这样一种思想: 在 Java 平台上编程要比使用直接编译成本机代码的语言更具动态性.如果您理解了这些动 态方面,就可以使用 Java 编程完成那些在任何其它主流编程语言中不能完成的事情. 本文中,我将讨论一些基本概念,它们是这些 Java 平台动态特性的基础.

Java编程的动态性,第8部分: 用代码生成取代反射

从本系列前面的文章中,您了解到反射的性能比直接访问要慢许多倍,并了解了用 Javassist 和 Apache Byte Code Engineering Library (BCEL)进行classworking.Java 顾问 Dennis Sosnoski 通过演示如何使用运行时 classworking,来用全速前进的生成代码 取代反射代码,从而结束他的 Java 编程的动态性系列. 既然您已经看到了如何使用 Javassist 和 BCEL 框架来进行 classworking ,我将展

Java编程的动态性, 第4部分: 用Javassist进行类转换

厌倦了只能按编写好源代码的方式执行的 Java 类了吗?那么打起精神吧,因为您就要发 现如何将编译器编译好的类进行改造的方法了!在本文中,Java 顾问 Dennis Sosnoski 通 过介绍字节码操作库 Javassist 将他的 Java 编程的动态性系列带入高潮,Javassist 是广 泛使用的 JBoss 应用服务器中加入的面向方面的编程功能的基础.您会看到到用 Javassist 转换现有类的基本内容,并且了解到这种用框架源代码处理类的方法的威力和局限性. 讲过了 Java 类格

Java编程的动态性,第2部分: 引入反射

在" Java编程的动态性,第1部分,"我为您介绍了Java编程类和类装入.该篇文章介绍 了一些Java二进制类格式的相关信息.这个月我将阐述使用Java反射API来在运行时接入和使 用一些相同信息的基础.为了使已经熟知反射基础的开发人员关注本文,我将在文章中包括 反射性能如何与直接接入相比较. 使用反射不同于常规的Java编程,其中它与 元数据--描述其它数据的数据协作.Java语 言反射接入的特殊类型的原数据是JVM中类和对象的描述.反射使您能够运行时接入广泛的类 信息.它甚至使您

Java编程的动态性,第6部分: 利用Javassist进行面向方面的更改

Java 顾问 Dennis Sosnoski 在他的关于 Javassist 框架的三期文章中将精华部分留在 了最后.这次他展现了 Javassist 对搜索-替换的支持是如何使对 Java 字节码的编辑变得 像文本编辑器的"替换所有(Replace All )"命令一样容易的.想报告所有写入特定字段 的内容或者对方法调用中参数的更改中的补丁吗?Javassist 使这变得很容易,Dennis 向您 展示了其做法. 本系列的 第 4 部分和 第 5 部分讨论了如何用 Javassis

Java编程的动态性,第7部分: 用BCEL设计字节码

Apache Byte Code Engineering Library (BCEL)可以深入 Java 类的字节码.可以用它 转换现有的类表示或者构建新的类,因为 BCEL 在单独的 JVM 指令级别上进行操作,所以可 以让您对代码有最强大的控制.不过,这种能力的代价是复杂性.在本文中,Java 顾问 Dennis Sosnoski 介绍了 BCEL 的基本内容,并引导读者完成一个示例 BCEL 应用程序,这 样您就可以自己决定是否值得以这种复杂性来换取这种能力. 在本系列的最后三篇文章中,我

Java编程的动态性,第3部分: 应用反射

命令行参数处理是一项令人厌烦的零碎工作,不管您过去已经处理过多少次了,它好像总 能重新摆在您的面前.与其一遍又一遍地编写同一块代码的不同变种,为什么不利用反射来 简化参数处理的工作呢?Java 顾问 Dennis Sosnoski 向您展示了如何做到这一点.在本文 中,Dennis 简明扼要地介绍了一个开源库,这个库可以使得命令行参数实际上自己处理自己 . 在 上个月的文章中,我介绍了Java Reflection API,并简要地讲述了它的一些基本功能 .我还仔细研究了反射的性能,并且在文章的

Java编程那些事儿86——文件操作之File类使用

11.3 I/O类使用 由于在IO操作中,需要使用的数据源有很多,作为一个IO技术的初学者,从读写文件开始学习IO技术是一个比较好的选择.因为文件是一种常见的数据源,而且读写文件也是程序员进行IO编程的一个基本能力.本章IO类的使用就从读写文件开始. 11.3.1 文件操作 文件(File)是最常见的数据源之一,在程序中经常需要将数据存储到文件中,例如图片文件.声音文件等数据文件,也经常需要根据需要从指定的文件中进行数据的读取.当然,在实际使用时,文件都包含一个的格式,这个格式需要程序员根据需要

Java编程思想里的泛型实现一个堆栈类 分享

觉得作者写得太好了,不得不收藏一下. 对这个例子的理解: //类型参数不能用基本类型,T和U其实是同一类型. //每次放新数据都成为新的top,把原来的top往下压一级,通过指针建立链接. //末端哨兵既是默认构造器创建出的符合end()返回true的节点. 复制代码 代码如下: //: generics/LinkedStack.java // A stack implemented with an internal linked structure. package generics; pub