Java注解

使用注解
  什么是注解
  注解的作用
  编译器使用的注解
  注解定义配置参数
  注解配置注意事项
  总结
定义注解
  简介
  元注解
    @Target
    @Retention
    生命周期
    @Repeatable
    @Inherited
  定义Annotation步骤
  总结
处理注解
  读取RUNTIME类型注解
  isAnnotationPresent
  getAnnotation
  读取注解的方式
  getParameterAnnotations()
  总结

使用注解

什么是注解

注解是放在Java源码的类、方法、字段、参数前的一种标签

@Resource("hello") // 注解
public class Hello {
  	@Inject	// 注解
  	int n;
  	
  	@PostConstruct // 注解
  	public void hello(@Param String name) {
      	System.out.println(name);
    }
  	@Override // 注解
  	public String toString() {
      	return "hello";
    }
}
注解的作用
  • 注解本身对代码逻辑没有任何影响
  • 如何使用注解由工具来决定
编译器使用的注解
  • @Override 让编译器检查该方法是否正确地实现了覆写
@Override
public String toString() {
  	return "hello";
}
  • @Deprecated 告诉编译器该方法已经被标记为“作废”,在其他地方饮用将会出现编译警告
@Deprecated
public void hello(String name) {
  	System.out.println(name);
}
  • @SuppressWarnings 忽略警告
@SuppressWarnings
public void hello() {
  	int n; // local variable n is not used;
}
注解定义配置参数
  • 配置参数由注解类型定义
  • 配置参数可以包括
    • 所有基本类型
    • String
    • 枚举类型
    • 数组
  • 配置参数值必须是常量
注解配置注意事项
  • 缺少某个配置参数将使用默认值

  • 如果只写常量,相当于省略了value参数

  • 如果只写注解,相当于全部使用默认值

总结
  • 注解(Annotation)是Java语言用于工具处理的标注
  • 注解可以配置参数,没有指定配置的参数使用默认值
  • 如果参数名称是value,可以省略参数名称

定义注解

简介

使用@interface定义注解(Annotation)

  • 注解的参数类似无参数方法
  • 可以设定一个默认值(推荐)
  • 把最常用的参数命名为value(推荐)
public @interface Report {
  	int type() default 0;
  	String level() default "info";
  	String value() default "";
}
元注解

可以修饰其他注解的注解称为元注解

@Target

使用@Target定义Annotation指定注解可以被应用于源码的哪些位置,可定义单个或多个:

  • 类或接口:ElementType.TYPE
  • 字段:ElementType.FIELD
  • 方法:ElementType.METHOD
  • 构造方法:ElementType.CONSTRUCTOR
  • 方法参数:ElementType.PARAMETER
//@Target(ElementType.METHOD) 
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface Report {
  	int type() default 0;
  	String level() default "info";
  	String value() default "";
}
@Retention

使用R@Retention定义Annotation的生命周期:

  • 仅编译期:RetentionPolicy.SOURCE
  • 仅class文件:RetentionPolicy.CLASS
  • 运行期:RetentionPolicy.RUNTIME

如果@Retention不存在,则该Annotation默认为CLASS

通常自定义的Annotation都是RUNTIME

@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
  	int type() default 0;
  	String level() default "info";
  	String value() default "";
}
生命周期

Annotation的生命周期

  • RetentionPolicy.SOURCE:编译器在编译时直接丢弃注解
  • RetentionPolicy.CLASS:该Annotation仅存储在class文件中
  • RetentionPolicy.RUNTIME:在运行期可以读取该Annotation
@Repeatable

使用@Repeatable定义Annotation是否可重复

  • JDK >= 1.8
// 定义注解,设置可重复
@Repeatable
@Target(ElementType.TYPE)
public @interface Report {
  	int type() default 0;
  	String level() default "info";
		String value() default "";
}
// 使用注解
@Report(type=1, level="debug")
@Report(type=2, level="warning")
public class Hello {
  
}
@Inherited

使用@Inherited定义子类是否可继承父类定义的Annotation

  • 仅针对@Target为TYPE类型的Annotation
  • 仅针对class的继承
  • 对interface的继承无效
// 定义注解,设置可继承
@Inherited
@Target(ElementType.TYPE)
public @interface Report {
  	int type() default 0;
  	String level() default "info";
		String value() default "";
}
// 在父类使用注解
public class Person { 	
}
// 子类自动会继承父类的Report注解
public class Student extends Person {
}
定义Annotation步骤
  • 用@interface定义注解
  • 用元注解(meta annotation)配置注解
    • Target:必须设置
    • Retention:一般设置为RUNTIME
    • 通常不必写@Inherited,@Repeatable等等
  • 定义注解参数和默认值
// 2.1
@Target(ElementType.TYPE)
// 2.2
@Retention(RetentionPolicy.RUNTIME)
// 1
public @interface Report {
  	// 3
  	int type() default 0;
  	String level() default "info";
		String value() default "";
}
总结
  • 使用@interface定义注解
  • 可定义多个参数和默认值,核心参数使用value名称
  • 必须是指@Target来制定Annotation可以应用的范围
  • 应当设置@Retention为RUNTIME便于运行期读取该Annotation

处理注解

读取RUNTIME类型注解
  • Annotation也是class
  • 所有Annotation继承自java.lang.annotation.Annotation
  • 使用反射API读取
isAnnotationPresent

isAnnotationPresent(Annotation.class)方法用于判断某个注解是否存在

  • Class.isAnnotationPresent(Class)
  • Field.isAnnotationPresent(Class)
  • Method.isAnnotationPresent(Class)
  • Constructor.isAnnotationPresent(Class)
Class cls = Person.class;
// 判断Person类是否存在Report注解
cls.isAnnotationPresent(Report.class);
getAnnotation

getAnnotation(Annotation.class)用于获取某个注解

  • Class.getAnnotation(Class)
  • Field.getAnnotation(Class)
  • Method.getAnnotation(Class)
  • Constructor.getAnnotation(Class)
Class cls = Person.class;
// 获取注解,如果没有返回null
Report report = cls.getAnnotation(Report.class);
// 获取注解的值
int type = report.type();
String level = report.level();
读取注解的方式
Class cls = Person.class;
// 1、先判断是否有注解
if (cls.isAnnotationPresent(Report.class)) {
  	// 2、获取注解
  	Report report = cls.getAnnotation(Report.class);
  	...
}
Class cls = Person.class;
// 1、直接获取注解
Report report = cls.getAnnotation(Report.class);
// 2、判断是否为null
if (report != null) {
  	...
}
getParameterAnnotations()

getParameterAnnotations()方法用于读取方法参数中的注解,返回结果是一个二位数组,每个参数的注解是一个一维数组。

// 方法参数使用注解
public String hello(@Notnull @Range(max=5) String name, @NotNull String prefix) {
	  ... 
}

Method m = ...
// 获取方法参数中的所有注解
Annotation[][] annos = m.getParameterAnnotations();
// 获取第一个参数中的所有注解
Annotation[] annosOfName = annos[0];// name参数的Annotation
for (Annotation anno : annosOfName) {
  	if (anno instanceof Range) {
      	Range r = (Range)anno;
      	... 
    }
}
// 注解二位数组
{
  	{@NotNull, @Range},
  	{@NotNull}
}

总结
  • 可以在运行期通过反射读取RUNTIME类型的注解,不要漏写@Retention(RetentionPolicy.RUNTIME)
  • 可以通过工具处理注解来实现相应的功能
    • 对JavaBean的属性值按规则进行检查
    • JUnit会自动运行@Test注解的测试方法
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章