Java注解

Java注解

Annotations是一种元数据,其作用在于提供程序本身以外的一些数据信息,也就是说Annotation他不会属于程序代码本身,不参与逻辑运算,故而不会对原程序代码的操作产生直接的影响。

  • 为编译器提供辅助信息 — Annotations可以为编译器提供而外信息,以便于检测错误,抑制警告等.
  • 编译源代码时进行而外操作 — 软件工具可以通过处理Annotation信息来生成原代码,xml文件等等.
  • 运行时处理 — 有一些annotation甚至可以在程序运行时被检测,使用.

元注解

元注解的作用就是负责注解其他注解。它们被用来提供对其它 annotation类型作说明。Java定义的元注解:

  • @Target
  • @Retention
  • @Documented
  • @Inherited
  • @Native
  • @Repeatable

@Target

@Target 指明了注解修饰的对象范围。@Target 接受参数类型为 java.lang.annotation.ElementType。ElementType 包含以下Enum 值:

  • ANNOTATION_TYPE: 用于描述注解类型
  • CONSTRUCTOR: 用于描述构造器
  • FIELD: 用于描述域,包括Enum常量
  • LOCAL_VARIABLE: 用于描述局部变量
  • METHOD: 用于描述方法
  • PACKAGE: 用于描述包
  • PARAMETER: 用于描述参数
  • TYPE: 用于描述类,接口(包括注解类型)或Enum类型
  • TYPE_PARAMETER: 用于描述泛型
  • TYPE_USE: 用于描述类型检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*注解Table 可以用于注解类、接口(包括注解类型) 或enum声明
*而注解NoDBColumn仅可用于注解类的成员变量。
*/
@Target(ElementType.TYPE)
public @interface Table {
/**
* 数据表名称注解,默认值为类名称
* @return
*/
public String tableName() default "className";
}

@Target(ElementType.FIELD)
public @interface NoDBColumn {

}

@Retention

@Retention定义了该Annotation被保留的时间长短:某些Annotation仅出现在源代码中,而被编译器丢弃;而另一些却被编译在class文件中;编译在class文件中的Annotation可能会被虚拟机忽略,而另一些在class被装载时将被读取(请注意并不影响class的执行,因为Annotation与class在使用上是被分离的)。使用这个meta-Annotation可以对 Annotation的“生命周期”限制。取值(java.lang.annotation.RetentionPolicy)有:

  • SOURCE:在源文件中有效(即源文件保留)
  • CLASS:在class文件中有效(即class保留)
  • RUNTIME:在运行时有效(即运行时保留)

1
2
3
4
5
6
7
8
9
10
11
/* Column注解的的RetentionPolicy的属性值是RUTIME,
* 这样注解处理器可以通过反射,获取到该注解的属性值.
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}

@Documented

@Documented注解表示每当使用指定的注解时,使用Javadoc工具记录这些元素。默认情况下,注解不包括在Javadoc中。 @Documented 是一个标记注解,没有成员。

1
2
3
4
5
6
7
8
9
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
public String name() default "fieldName";
public String setFuncName() default "setField";
public String getFuncName() default "getField";
public boolean defaultDBValue() default false;
}

@Inherited

@Inherited元注解是一个标记注解,@Inherited表示被标注的注解是可被继承的。如果一个使用@Inherited修饰的注解被用于一个class,则这个annotation将被用于该class的子类。

1
2
3
4
5
6
7
@Target({ElementType.METHOD, ElementType.TYPE})
@Inherited
public @interface Greeting {
public enum FontColor{ BULE,RED,GREEN};
String name();
FontColor fontColor() default FontColor.GREEN;
}

注意,@Inherited 只能让子类获取父类标记在Type上的注解。如果注解标记在父类方法上,子类覆盖的方法无法获取到注解。如果子类实现一个或多个接口,也不会从它实现的接口继承任何注释。

想实现方法上注解的继承,你可以通过反射在继承链上找到方法上的注解,或者Spring也提供了AnnotationUtils用于处理Annotation。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public static <A extends Annotation> A getInheritedAnnotation(
Class<A> annotationClass, AnnotatedElement element)
{
A annotation = element.getAnnotation(annotationClass);
if (annotation == null && element instanceof Method)
annotation = getOverriddenAnnotation(annotationClass, (Method) element);
return annotation;
}

private static <A extends Annotation> A getOverriddenAnnotation(
Class<A> annotationClass, Method method)
{
final Class<?> methodClass = method.getDeclaringClass();
final String name = method.getName();
final Class<?>[] params = method.getParameterTypes();

// prioritize all superclasses over all interfaces
final Class<?> superclass = methodClass.getSuperclass();
if (superclass != null)
{
final A annotation =
getOverriddenAnnotationFrom(annotationClass, superclass, name, params);
if (annotation != null)
return annotation;
}

// depth-first search over interface hierarchy
for (final Class<?> intf : methodClass.getInterfaces())
{
final A annotation =
getOverriddenAnnotationFrom(annotationClass, intf, name, params);
if (annotation != null)
return annotation;
}

return null;
}

private static <A extends Annotation> A getOverriddenAnnotationFrom(
Class<A> annotationClass, Class<?> searchClass, String name, Class<?>[] params)
{
try
{
final Method method = searchClass.getMethod(name, params);
final A annotation = method.getAnnotation(annotationClass);
if (annotation != null)
return annotation;
return getOverriddenAnnotation(annotationClass, method);
}
catch (final NoSuchMethodException e)
{
return null;
}
}

@Native

用于表示可从Native方法获取的常量属性。

@Repeatable

表示标记的注释可以多次应用于同一声明或类型使用,即重复注解。

重复注解示例

1
2
3
4
// 在每月最后一天和每周五 11:00 pm 执行doPeriodicCleanup
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }

为了实现重复注解,需要下面两步:

声明一个可重复注解类型

1
2
3
4
5
6
7
8
import java.lang.annotation.Repeatable;

@Repeatable(Schedules.class)
public @interface Schedule {
String dayOfMonth() default "first";
String dayOfWeek() default "Mon";
int hour() default 12;
}

@Repeatable 注解的值是注解容器的类型,java 编译器用它来存储重复注解。

声明该类型注解容器

1
2
3
public @interface Schedules {
Schedule[] value();
}

注解容器必须拥有数组类型的变量属性,数组元素类型必须是重复注解的类型。

Java 自身使用的注解

在 java.lang 中使用了下面5个注解:

@Deprecated

@Deprecated 注解表示标记的元素已被弃用,不应再使用。

使用例子:

1
2
3
4
5
6
/ **
* @deprecated
* 解释为什么它被弃用
* /
@Deprecated
static void deprecatedMethod(){}

@Override

@Override 注解表示意图覆盖超类中声明的元素。

1
2
3
// 标记方法作为超类方法已被覆盖
@Override
int overriddenMethod(){}

@SuppressWarnings

@SuppressWarnings 注释告诉编译器压制它将会生成的特定警告。

1
2
3
4
5
6
// 使用已弃用的方法,告诉编译器不生成警告
@SuppressWarnings(“deprecation”)
void useDeprecatedMethod(){
//弃用警告
objectOne.deprecatedMethod();
}

@SafeVarargs

@SafeVarargs 注解在应用于方法或构造函数时,声明该代码对其参数不执行潜在的不安全操作。

@FunctionalInterface

@FunctionalInterface 注解,在Java SE 8引入,表示该类型声明意在成为功能的接口,由Java语言规范所定义的。


参考资料: