使用注解
什么是注解
注解的作用
编译器使用的注解
注解定义配置参数
注解配置注意事项
总结
定义注解
简介
元注解
@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注解的测试方法