技术开发 频道

Java深度历险之Java字节代码的操纵

  下面介绍一个例子,是关于如何在Java里面做四则运算,比如求出来(3+4)*7-10的值。一般的做法是分析输入的运算表达式,自己来模拟计算过程。考虑到括号的存在和运算符的优先级等问题,这样的计算过程会比较复杂,而且容易出错。另外一种做法是可以用JSR 223引入的脚本语言支持,直接把输入的表达式当做JavaScript或是JavaFX脚本来执行,得到结果。下面的代码使用的做法是动态生成Java源代码并编译,接着加载Java类来执行并获取结果。这种做法完全使用Java来实现。

private static double calculate(String expr) throws CalculationException  {
  
String className = "CalculatorMain";
  
String methodName = "calculate";
  
String source = "public class " + className
      
+ " { public static double " + methodName + "() { return " + expr + "; } }";
      
//省略动态编译Java源代码的相关代码,参见上一节
  
boolean result = task.call();
  
if (result) {
      ClassLoader loader
= Calculator.class.getClassLoader();
      try {            
         Class
<?> clazz = loader.loadClass(className);
         Method method
= clazz.getMethod(methodName, new Class<?>[] {});
        
Object value = method.invoke(null, new Object[] {});
         return (
Double) value;
      } catch (Exception e) {
         throw
new CalculationException("内部错误。");        
      }    
   }
else {
      throw
new CalculationException("错误的表达式。");    
   }
}

  上面的代码给出了使用动态生成的Java字节代码的基本模式,即通过类加载器来加载字节代码,创建Java类的对象的实例,再通过Java反射API来调用对象中的方法。

  Java字节代码增强

  Java 字节代码增强指的是在Java字节代码生成之后,对其进行修改,增强其功能。这种做法相当于对应用程序的二进制文件进行修改。在很多Java框架中都可以见到这种实现方式。Java字节代码增强通常与Java源文件中的注解(annotation)一块使用。注解在Java源代码中声明了需要增强的行为及相关的元数据,由框架在运行时刻完成对字节代码的增强。Java字节代码增强应用的场景比较多,一般都集中在减少冗余代码和对开发人员屏蔽底层的实现细节上。用过JavaBeans的人可能对其中那些必须添加的getter/setter方法感到很繁琐,并且难以维护。而通过字节代码增强,开发人员只需要声明Bean中的属性即可,getter/setter方法可以通过修改字节代码来自动添加。用过JPA的人,在调试程序的时候,会发现实体类中被添加了一些额外的 域和方法。这些域和方法是在运行时刻由JPA的实现动态添加的。字节代码增强在面向方面编程(AOP)的一些实现中也有使用。

  在讨论如何进行字节代码增强之前,首先介绍一下表示一个Java类或接口的字节代码的组织形式。

类文件 {
   0xCAFEBABE,小版本号,大版本号,常量池大小,常量池数组,
   访问控制标记,当前类信息,父类信息,实现的接口个数,实现的接口信息数组,域个数,
   域信息数组,方法个数,方法信息数组,属性个数,属性信息数组
}
0
相关文章