程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java5.0的元注解(meta-annotations)

Java5.0的元注解(meta-annotations)

編輯:關於JAVA

許多API都需要為數不少的“模板式”的代碼,比如,如果想寫一個JAX-RPC(JAX-RPC即Java API for XML-Based RPC-譯注)的web service,你需要提供一對接口及其實現程序。如果程序本身能被表明哪些方法可以由遠程訪問的注釋所“修飾”的話,那麼這種“模板式”的代碼就可以用工具自動生成。

另一些API需要在維護程序的同時維護一些“輔助”文件,比如,維護JavaBean的同時需要維護BeanInfo類,Enterprise JavaBeans (EJB) 則需要部署說明檔(deployment descriptor)。如果這些輔助信息能在程序中作為注解來維護,就會方便地多,並且也不容易犯錯。

Java平台已經提供了幾種不同的注解機制。比如transIEnt修飾符就是一種特別的注解,標明被修飾的字段在序列化的子系統應該被忽略,@deprecated 作為javadoc的特殊標簽,標明該方法不應該再用。在5.0版本中,Java平台提供了一種一般性的注解(即元數據metadata)機制,允許你自定義注解類型和使用該類型。該機制包含注解的定義語法、聲明語法、讀取注解的API、注解的代理類文件以及注解的一個處理工具。

注解並不直接對程序語義產生影響,但是會對工具或庫在處理程序的方式上時產生影響,進而影響程序在運行時的語義。注解可以從源文件或類文件中讀取,甚至在運行時以反射方式讀取。

注解是對javadoc標簽的補充。一般而言,如果標注的目的是影響或產生文檔,那麼就該用Javadoc標簽,否則就用注解。典型的應用程序的程序員永遠不必自己定義一個注解類型,但其實真要做也不難。注解類型的定義跟接口定義相似,只是需要在inteface關鍵字前面加一個at符(@),聲明一個方法即為注解類型定義一個元素。方法聲明時不允許有參數或throw語句,返回值類型被限定為原始數據類型、字符串String、Class、枚舉enums、注解類型,或前面這些的數組,方法可以有默認值,下面是一個定義注解類型的例子:

/**
 * Describes the Request-For-Enhancement(RFE) that led
 * to the presence of the annotated API element.
 */
public @interface RequestForEnhancement {
    int    id();
    String synopsis();
    String engineer() default "[unassigned]"; 
    String date();    default "[unimplemented]"; 
}

一旦完成定義,就可以使用它做注解聲明。注解是一個特殊的修飾符,可以和其他修飾符一樣(如public,static,final)用在各種地方。為方便計,注解一般放在最前面。注解修飾符由@符號、注解類型名和括號括起來的元素名/值對組成。下面這個例子聲明一個方法使用了上面所定義的注解。

@RequestForEnhancement(
    id       = 2868724,
    synopsis = "Enable time-travel",
    engineer = "Mr. Peabody",
    date     = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }

注解類型如果不含任何元素,被稱為“標記”注解類型,如:

/**
 * Indicates that the specification of the annotated API element
 * is preliminary and subject to change.
 */
public @interface Preliminary { }

“標記”注解類型允許忽略括號,如下:

@Preliminary public class TimeTravel { ... }

如果注解只有一個元素,那麼該元素應該被命名為value,如下:

/**
 * Associates a copyright notice with the annotated API element.
 */
public @interface Copyright {
    String value();
}

如果注解只有一個元素,並且元素名為value,那麼元素名和等號(=)都可以忽略不寫,如下:

@Copyright("2002 Yoyodyne Propulsion Systems")
public class OscillationOverthruster { ... }

我們將建一個簡單的基於注解的測試框架,將前面所講的合起來用。首先我們定義一個“標記”注解類型,來說明某個方法是一個測試方法,需要被測試工具執行:

import Java.lang.annotation.*;

/**
 * Indicates that the annotated method is a test method.
 * This annotation should be used only on parameterless static methods.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test { }

注意這個注解本身也是被注解的。這些注解被稱為“元注解” (meta-annotations). 第一個(@Retention(RetentionPolicy.RUNTIME)) 表示注解由虛擬機保留,可以在運行時通過反射讀取;第二個(@Target(ElementType.METHOD)) 表示注解只能用在方法上。

下面是一個例子程序,其中的一些方法由上面所定義的注解來修飾:

public class Foo {
    @Test public static void m1() { }
    public static void m2() { }
    @Test public static void m3() {
        throw new RuntimeException("Boom");
    }
    public static void m4() { }
    @Test public static void m5() { }
    public static void m6() { }
    @Test public static void m7() {
        throw new RuntimeException("Crash");
    }
    public static void m8() { }
}

下面是測試工具程序:

import Java.lang.reflect.*;

public class RunTests {
   public static void main(String[] args) throws Exception {
      int passed = 0, failed = 0;
      for (Method m : Class.forName(args[0]).getMethods()) {
         if (m.isAnnotationPresent(Test.class)) {
            try {
               m.invoke(null);
               passed++;
            } catch (Throwable ex) {
               System.out.printf("Test %s failed: %s %n", m, ex.getCause());
               failed++;
            }
         }
      }
      System.out.printf("Passed: %d, Failed %d%n", passed, failed);
   }
}

這個工具程序將一個類名作為命令行運行時的參數,然後遍歷該類的所有方法,並嘗試調用每一個被“Test”注解(就是上面所定義的)所修飾的方法。那行綠色的代碼以反射調用方式查找方法是否被“Test”注解所修飾。如果調用測試方法時拋出異常,就認為測試失敗,測試失敗的信息就會打印出來。最後作為總結,測試失敗的方法個數和測試成功的方法個數也會打印出來。下面是對Foo程序(就是上面那個)運行測試工具得到的結果:

$ java RunTests Foo
Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom 
Test public static void Foo.m7() failed: Java.lang.RuntimeException: Crash 
Passed: 2, Failed 2

這個測試工具無疑是個玩具程序,但它表明了注解的強大,並能很容易的擴展為一個有用的工具。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved