Welcome 微信登录

首页 / 软件开发 / JAVA / Classworking工具箱: 注释(Annotation)与ASM

Classworking工具箱: 注释(Annotation)与ASM2011-06-27 IBM Dennis Sosnoski到 J2SE 5.0,Sun 已经给 Java 平台添加了许多新特性。最为重要的一个新特性是支持注释。注释在关联多种类型的元数据与 Java 代码方面将会很有用,并且在扩展 Java 平台的新的和更新的 JSR 中,它已经被广泛用来代替定制配置文件。在本文中,我将向您展示如何结合使用 ASM 字节码操作框架和 J2SE 5.0 的新增特性 —— instrumentation 包 —— 来在类被加载到 JVM 中时,按照注释的控制来转换类。

注释基础知识

讨论 J2SE 5.0 注释的文章已经很多了,所以在此我只作一个简短的归纳。注释是一种针对 Java 代码的元数据。在功能上类似于日益普及的用于处理复杂框架配置的 XDoclet 样式的元数据,而其实现则与 C# 属性有更多的共同点。

该语言特性的 Java 实现使用一种类似于接口的结构和 Java 语言语法的一些特殊扩展。我发现大多数情况下忽略这种类似于接口的结构,而把注释看作是名值对的 hashmap 会更清晰。每个注释类型定义了一组与之关联的固定名称。每个名称可能被赋予一个默认值,否则的话每次使用该注释时都要定义该名称。注释可以被指定应用于一种特定类型的 Java 组件(如类、字段、方法,等等),甚至还可以应用于其他的注释。(实际上,您是通过在要限制的注释的定义上使用一个特殊的预定义注释,来限制注释适用的组件的。)

不同于常规接口,注释必须在定义中使用关键字 @interface。同样不同于常规接口的是,注释只能定义不带参数且只返回简单值(基本类型、String、 Class、enum 类型、注释,以及任意这些类型的数组)的“方法”。这些“方法”是与注释关联的值的名称。

注释被用作声明时的修饰符,就像 public、final,以及其他早于 J2SE 5.0 版本的 Java 语言所定义的关键字修饰符。注释的使用是由 @ 符号后面跟注释名来表明的。如果要给注释赋值,在注释名后面的圆括号中以名值对的形式给出。

清单 1 展示了一个示例注释声明,后面是将该注释用于某些方法的类的定义。该 LogMe 注释用来标记应该包含在应用程序的日志记录中的方法。我已经给该注释赋了两个值:一个表示该调用被包含其中的日志记录的级别,另一个表示用于该方法调用的名称(默认是空字符串,假定没有名称时,处理该注释的代码将代入实际的方法名)。然后我将该注释用于 StringArray 类中的两个方法,对 merge() 方法只使用默认值,对 indexOf() 方法则提供显式值。

清单 1. 反射代替接口及其实现

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
/**
* Annotation for method to be included in logging.
*/
@Target({ElementType.METHOD})
public @interface LogMe {
int level() default 0;
String name() default "";
}
public class StringArray
{
private final String[] m_list;

public StringArray(String[] list) {
...
}

public StringArray(StringArray base, String[] adds) {
...
}

@LogMe private String[] merge(String[] list1, String[]list2) {
...
}

public String get(int index) {
return m_list[index];
}

@LogMe(level=1, name="lookup") public int indexOf(String value) {
...
}

public int size() {
return m_list.length;
}
}

下一小节我将介绍一个不同的(我认为是更有趣的)应用程序。