声明
本译文同步发表在译言
“软件设计与开发”小组,“软件设计与开发”小组关注软件设计思想,软件开发模式等最新前沿文章的翻译,有兴趣的请加入。
许多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
这个测试工具无疑是个玩具程序,但它表明了注解的强大,并能很容易的扩展为一个有用的工具。
分享到:
相关推荐
2 ... 2 ... 5 ... 2 ... 7 ... 8 ...
这个一个自定义注解实现给注解传递伪动态参数的小案例,可以在此自定义注解的基础上来记录接口的调用记录。
Offers seamlessly-integrated explanations of Java 5.0's key innovations, from generics to annotations Shows how TDD impacts system design, and vice versa Complements any agile or traditional ...
GraphQL-Java批注 是一个很棒的库,但是它的语法有点冗长。... compile " io.github.graphql-java:graphql-java-annotations:8.3 " } (Maven语法) < groupId> io . github . graphql - java
annotations-java8.jar;annotations-java8.jar;annotations-java8.jar
主要介绍了Java8新特性之重复注解(repeating annotations)浅析,这个新特性只是修改了程序的可读性,是比较小的一个改动,需要的朋友可以参考下
5.0 standard annotations, the apt tool for source-level annotation processing, and bytecode engineering Advanced Swing and AWTlists, trees, tables, and other advanced components; image processing ...
Android annotations 注解 使用,一些使用的注解实现更好的编程
tpch.zip,TPC-H DBGEN到Java端口TPC-H DBGEN到Java端口
以IntelliJ IDEA和Eclipse为例,这些IDE工具为JDK 5.0注解功能提供了自动完成和语法高亮功能. 注解被直接编译到字节码里,并 在运行时(对于Hibernate来讲就是启动的时候)通过反射读取这些注解, 因此外部XML文件就...
Java Annotation详解 Java Annotation详解 Java Annotation详解
Hibernate Annotations API 中文文档 前言 1. 创建一个注解项目 1.1. 系统需求 1.2. 系统配置 2. 实体Bean 2.1. 简介 2.2. 用EJB3注解进行映射 2.2.1. 声明实体bean 2.2.1.1. 定义表(Table) 2.2.1.2. 乐观...
annotations-api.jar注解
标签:swagger、annotations、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心...
类、方法、变量、参数、包都可以被注解,可用来将信息元数据与程序元素进行关联 An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and ...
JDK 5.0的新特性Annotations
适用于旧版Java 5.0兼容软件包的 使用注释 注释发布在。 要使用gradle添加依赖项,请在build.gradle文件中编写以下内容: dependencies { compileOnly 'org.jetbrains:annotations:20.1.0' } 要使用Maven添加依赖...
第十八章:Spring 注解小马哥 · mercyblitzSpring 注解Spring 注解驱动编程发展历程Spring 核心注解场景分类Spring 注解
一组Java注释,可以在基于JVM的语言中使用。它们作为附加文档,可以是
标签:swagger、annotations、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构保持不变,注释和说明精准翻译,请放心...