0%

注解

在Spring的学习中,总发现自己基础不扎实,JavaSE知识有所欠缺,所以决定抽空补一下,希望能拾起来忘掉的东西

一、注解的使用

注解的作用

  • 注解本身对代码逻辑没有任何影响
  • 如何使用注解由工具决定

编译器使用的注解

  1. @Override:让编译器检查该方法是否正确的实现了覆写
  2. @Deprecated:告诉编译器该方法已经标记为“作废”,在在其他地方引用将会出现编译警告
  3. @SuppressWarnings:让编译器忽略代码块中的编译警告

注解可以定义配置参数

  1. 配置参数由注解类型定义
  2. 配置参数可以包括:
    • 基本类型
    • String
    • 枚举类型
    • 数组
  3. 配置参数必须是常量

注解的使用

  • 缺少某个配置参数将使用默认值
  • 如果只写常量,相当于使用了value参数
  • 如果只写注解,相当于全部使用默认值

二、定义注解

使用@Interface定义注解

  • 注解的参数类似于无参数方法
  • 可以设定一个默认值(推荐)
  • 把最常用的参数命名为value(推荐)
1
2
3
4
5
6
public @Interface Report{
int type() default 0;

String level() default "info";
String value() default "";
}

元注解(用来修饰其他注解)

  1. @Target()

    使用@Target定义Annotation可以被应用于源码的哪些位置

    • 类或接口:ElementType.TYPE
    • 字段:ElementType.FIELD
    • 方法:ElementType.METHOD
    • 构造方法:ElementType.CONSTRUCTOR
    • 方法参数:ElementType.PARAMETER
1
2
3
4
5
6
7
8
9
10
@Target({
ElementType.TYPE,
ElementType.FIELD
})
public @Interface Report{
int type() default 0;

String level() default "info";
String value() default "";
}
  1. @Retention()

    使用@Retention定义Annotation生命周期

    • RetentionPolicy.RUNTIME,运行期,在运行期可以用代码读取该Annotation
    • RetentionPolicy.SOURCE,仅编译期,编译器在编译时直接丢弃,例如@Override
    • RetentionPolicy.CLASS,仅class文件,该注解仅储存在class文件中,非常少见

    通常自定义的Annotation都是RUNTIME,不要漏写

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

    1
    2
    3
    4
    5
    6
    7
    @Retention(RetentionPolicy.RUNTIME)
    public @Interface Report{
    int type() default 0;

    String level() default "info";
    String value() default "";
    }
  2. @Repeatable

    使用@Repeatable定义Annotation是否可重复(JDK>=1.8)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Retention(RetentionPolicy.RUNTIME)
    public @Interface Report{
    int type() default 0;

    String level() default "info";
    String value() default "";
    }

    @Report(type=1,level="debug")
    @Report(type=2,levle="warning")
    public class fun(){
    //do something...
    }
  3. Inherited

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

    • 仅针对@TargetTYPE类型的Annotation
    • 仅针对class的继承,对interface的继承无效

    定义Annotation的步骤:

    1. @interface定义注解
    2. 用元注解(meta annotation)配置注解
      • Target:必须设置
      • Retention:一般设置为RUNTIME
      • 通常不必写@Inherited,@Repeatable
    3. 定义注解参数和默认值
    1
    2
    3
    4
    5
    6
    7
    8
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @Interface Report{
    int type() default 0;

    String level() default "info";
    String value() default "";
    }

三、处理注解

在代码中我们使用反射机制来读取@Retention为RUNTIME的注解

所有的Annotation继承自java.lang.annotation.Annotation

可以通过工具处理注解来实现相应的功能

  • 对JavaBean的属性值按规则进行检查
  • JUnit会自动运行@Test注解的测试方法

反射API:

判断注解是否存在

  • Class.isAnnotationPresent(Class)
  • Field.isAnnotationPresent(Class)
  • Method.isAnnotationPresent(Class)
  • Constructor.isAnnotationPresent(Class)

获取注解,若该注解不存在则返回NULL

  • Class.getAnnotation(Class)
  • Field.getAnnotation(Class)
  • Method.getAnnotation(Class)
  • Constructor.getAnnotation(Class)
1
2
3
4
5
6
7
8
9
10
11
Class cls = Person.class;
if(cls.isAnnotationPresent(Report.class)){
Report report = cls.getAnnotation(Report.class);
//do something...
}
//--或者--
Class cls = Person.class;
Report report = cls.getAnnotation(Report.class);
if(report!=null){
//do something...
}

读取方法参数的Annotation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//这里@NotNull与@Range为自定义注解
public String hello(@NotNull @Range(max5) String name,@NotNull String prefix){
...
}

Method m = ...;
Range r;
//因为每一个参数可能有多个注解,一个方法本身又可以传入多个参数,故这里注解列表为二维数组
Annotation[][] annos = m.getParameterAnnotations();
//获得第一个参数的注解列表
Annotation[] annosOfName = annos[0];
//遍历
for(Annotation anno : annosOfName){
//取得第一个参数中类型为Range的注解
if(anno instanceof Range){
r = (Range)anno;
}
}