zoukankan      html  css  js  c++  java
  • Java Annotations初探

    以前刚开始学Java的时候,就听说了Java Annotations了,是在Java5中引入的,可是一直也没用到,所以也就没怎么研究了,最近在看Webx 3 (what is Webx ?) 的源代码,看到里面用到了这个东东,就准备把这个Annotation研究下。先看一下Java官网上怎么说的。这是个Java Annotation的介绍:http://download.oracle.com/javase/1.5.0/docs/guide/language/annotations.html,全英文,但是原汁原味,硬着头皮看吧,顺便用自己的理解把它汉化。

    =====begin========

    许多API需要大量的样本(boilerplate)代码,比如,如果我要写一个基于JAX_RPC的web service,那么我不得不提供一个配对的接口(interface)和实现。但是,如果我把这个程序中可以远程访问的方法用annotation来“装饰”起来,那么,这些样本代码就可以通过一个特定的工具来自动生成。

    另外,也有一些API需要同它的“附属文件”一块来维护。比如,JavaBeans 需要同时维护一个bean class和它对应的BeanInfo class,而EJB则需要同时维护一个部署描述文件(deployment descriptor)。而如果这些存在于附属文件中信息能够以“annotations”的形式保存在程序中,那么将会更加方便(因为两者在一处维护)和更加的不容易出错。

    Java平台(Java  plateform)本身就有许多专用的annotations机制。比如 “transient”修饰符(modifier)就是一个专用的annotation,用来表明被修饰的字段(field)在序列化(serialization)的时候被忽略。而@deprecated javadoc标记则是一个用来表明此方法不应该再被使用的专用annotation。自从Java5开始,Java平台提供了一个通用的annotation工具(也被称为metadata,元数据)来允许你定义和使用你自己的annotation类型。这些工具包括声明(declaring)annotation类型的语法,注解声明(annotating declarations)的语法,读取annotations的API,annotations的类文件的表示形式,以及annotation处理工具。

    annotations不会直接影响程序的语义,但是他们确实影响工具和库处理程序的方式,进而影响运行中的程序的语义。annotations可以从源文件 ,class文件,或者运行时的反射中读取。

    annotations扩充了javadoc tags。通常,如果标记(markup)是准备用来影响或者产生文档的,那么它应该可能是一个javadoc tag;否则,它应该是一个annotation。

    典型的应用开发程序员可能永远不必要自己定义一个annotation类型(type),但是,即使这么做,也不会太难。annotation类型声明(declarations)和普通的借口定义很相似,只不过在interface关键字前面要加一个“@”符号。接口中的声明的每一个方法定义了该annotation类型的一个元素(element)。方法声明中不能有任何的参数(parameter)或者throws 片段,返回的类型被限制在基本类型,String,Class,enums,annotations,和这些类型的数组形式。方法可以有默认的值(default values)。以下是一个annotation类型声明的例子:

    /**
    * 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]";
    }

    一旦一个annotation类型被定义,则就可以用它来注解声明(annotate declaration)了。一个annotation 是一种特殊的修饰符(modifier),可以被使用在其他修饰符(比如public,static,final)可以使用的任何地方。一般来说,惯例是把annotations放在其他修饰符的前面。annotation包括一个@,后加annotation类型和被小括号括起来的一系列"元素-值"对(list of element-value pairs). 这些值必须是编译时的常量(compile-time constants)。这里是带有上面定义的annotation类型对应的annotation的方法的定义( method declaration with an annotation corresponding to the annotation type declared above):

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

    一个没有元素的annotation类型被称为“标记annotation类型”(marker annotation type),比如:

    public @interface Preliminary { }

    对于这种“标记annotation类型”,在使用时,可以省略掉后边的圆括号。比如对于上面定义的annotation类型:

    @Preliminary public class TimeTravel { ... }

    对于只包含一个元素的annotation类型,这个元素的名字应该被命名为value,如下所示:

    public @interface Copyright {
    String value();
    }

    在这种情况下(单元素,元素名字为value),在使用时可以省略掉元素名字(即value)和“=”,比如:

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

    为了回顾一下我们上面学的,我们将会构建一个简单的基于annotation的测试框架。首先我们需要一个“标记annotation类型”(marker annotation type,还记得吗,就是没有元素的annotation类型)来表明一个方法是一个测试方法,这个方法应该被测试工具执行:

    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 { }

    注意,这个annotation类型声明本身也被注解(annotated)了,像这种类型的annotations叫做元注解(meta-annotations).第一个注解@Retention(RetentionPolicy.RUNTIME)表明这个注解类型是被虚拟机(VM)持有的,以便它们可以在运行时被反射的读取(read reflectively at run-time)。第二个注解@Target(ElementType.METHOD)则表明这个annotation类型只能被用来注解方法声明。

    下面是一个样例程序,一些方法(method)被上面提到的接口annotated了:

    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: %n%s %n", m, ex.getCause());
    failed++;
    }
    }
    }
    System.out.printf("Passed: %d, Failed %d%n", passed, failed);
    }
    }

    这个工具将一个class name作为命令行参数(command line argument),并且迭代这个class的所有方法,尝试调用带有@Test注解的方法。带下划线的部分表示通过反射方法来判定一个方法是否带有@Test注解。如果一个测试方法调用抛出一个异常,则被认定测试失败,并且打印失败report。最后,打印出测试成功和失败的数目信息。下面是当你利用Foo class运行测试工具时的运行结果:

    $ 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

    While this testing tool is clearly a toy, it demonstrates the power of annotations and could easily be extended to overcome its limitations.(尽管这个测试工具看上去像个玩具,但它却显示了annotations的power,并且可以很容易的扩展)。

    =====end=======

    额, 算了一下,边看边翻译,最后加在电脑上运行程序,一共花了2个小时(23:00-1:00)。虽然花的时间很多,但是好在这边讲的的弄明白了。

    睡觉了,明天还得上班呢。。。

  • 相关阅读:
    Kubernetes 集成研发笔记
    Rust 1.44.0 发布
    Rust 1.43.0 发布
    PAT 甲级 1108 Finding Average (20分)
    PAT 甲级 1107 Social Clusters (30分)(并查集)
    PAT 甲级 1106 Lowest Price in Supply Chain (25分) (bfs)
    PAT 甲级 1105 Spiral Matrix (25分)(螺旋矩阵,简单模拟)
    PAT 甲级 1104 Sum of Number Segments (20分)(有坑,int *int 可能会溢出)
    java 多线程 26 : 线程池
    OpenCV_Python —— (4)形态学操作
  • 原文地址:https://www.cnblogs.com/gaojing/p/2844973.html
Copyright © 2011-2022 走看看