java注解的使用

Java 注解 (Annotation)

三类注解

编译器使用到的注解

@Override,@SuppressWarnings都是编译器使用到的注解,作用是告诉编译器一些事情,而不会进入编译后的.class文件

.class文件使用到的注解

需要通过工具对.class字节码文件进行修改的一些注解,某些工具会在类加载的时候,动态修改用某注解标注的.class文件,从而实现一些特殊的功能,一次性处理完成后,并不会存在于内存中,都是非常底层的工具库、框架会使用,对于开发人员来说,一般不会涉及到

运行期读取的注解

一直存在于JVM中,在运行期间可以读取的注解,也是最常用的注解,如Spring的@Controller,@Service,@Repository,@AutoWired,Mybatis的@Mapper,Junit的@Test等,这类注解很多都是工具框架自定义在运行期间发挥特殊作用的注解,一般开发人员也可以自定义这类注解。

###定义Annotation

使用@interface来定义一个注解

1
2
3
4
5
6
7
8
9
10

public @interface Table {
String value() default "";
}

public @interface Colum {
String value() default "";
String name() default "";
String dictType() default "";
}

注解主要用到了String类型,但实际上还可以是基本数据类型(不能为包装类)、枚举类型。

约定俗成: 最常用的参数应该命名为value,同时一般情况下我们都会通过default参数设置一个默认值。

元注解

可以修饰注解的注解即为元注解,Java已经定义了一些元注解,我们可以直接使用

可以使用元注解来修饰注解,元注解包括多个,必须设置@Target@Retention@Retention一般设置为RUNTIME

@Target

指定注解使用的目标对象,参数为ElementType[]

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

public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}


// ElementType枚举中定义的属性

public enum ElementType {
/** 通过ElementType.TYPE可以修饰类、接口、枚举 */
TYPE,

/** 通过ElementType.FIELD可以修饰类属性 */
FIELD,

/** 通过ElementType.METHOD可以修饰方法 */
METHOD,

/** 通过ElementType.PARAMETER可以修饰参数(如构造器或者方法中的) */
PARAMETER,

/** 通过ElementType.CONSTRUCTOR可以修改构造器 */
CONSTRUCTOR,

/** 通过ElementType.LOCAL_VARIABLE可以修饰方法内部的局部变量 */
LOCAL_VARIABLE,

/** 通过ElementType.ANNOTATION_TYPE可以修饰注解 */
ANNOTATION_TYPE,

/** 通过ElementType.PACKAGE可以修饰包 */
PACKAGE,

/**
* 可以用在Type的声明式前
*
* @since 1.8
*/
TYPE_PARAMETER,

/**
* 可以用在所有使用Type的地方(如泛型、类型转换等)
*
* @since 1.8
*/
TYPE_USE
}

ElementType.PACKAGE

1
2
3
4
5

@Target(ElementType.PACKAGE)
public @interface Table {
String value() default "";
}

需要 创建package-info.java文件

1
2
3
4
5
6
7
@Table
package annotation;
class PackageInfo {
public void hello() {
System.out.println("hello");
}
}

还可以自行了解下 ElementType.TYPE_PARAMETER和ElementType.TYPE_USE*

@Retention

用于定义注解的生命周期,参数为枚举RetentionPolicy

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
Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}


public enum RetentionPolicy {
/**
* 仅存在于源代码中,编译阶段会被丢弃,不会包含于class字节码文件中.
*/
SOURCE,

/**
* 【默认策略】,在class字节码文件中存在,在类加载的时被丢弃,运行时无法获取到
*/
CLASS,

/**
* 始终不会丢弃,可以使用反射获得该注解的信息。自定义的注解最常用的使用方式。
*/
RUNTIME
}

@Documented

表示是否将此注解的相关信息添加到javadoc文档中

@Inherited

该注解和子类的关系,使用此注解声明出来的自定义注解,在使用在类上面时,子类会自动继承此注解,否则,子类不会继承此注解。注意,使用@Inherited声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Person {
String value() default "man";
}


@Person
public class Parent {
}
//子类也拥有@Person注解
class Son extends Parent {

}

Annotation处理

注解定义后也是一种class,所有的注解都继承自java.lang.annotation.Annotation,因此,读取注解,需要使用反射API。

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
//定义的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Colum {
String value() default "";
//用于表示某个属性代表的中文含义
String name() default "";
}

//用注解@Colum来修饰某个类的属性
public class Person {

@Colum(name = "姓名")
private String name;

@Colum(name = "性别")
private String gender;

@Colum(name = "年龄")
private int age;

@Colum(name = "住址")
private String address;

public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getGender() {return gender;}
public void setGender(String gender) {this.gender = gender;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
public String getAddress() {return address;}
public void setAddress(String address) {this.address = address;}
}

//通过反射读取这个类的所有字段的中文含义,并保存到list中,然后打印出来
public static void main(String[] args) throws ClassNotFoundException {
List<String> columNames = new ArrayList<>();
Class clazz = Class.forName("annotation.Person");
//获取Person类所有属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields){
//获取该属性的Colum注解
Colum colum = field.getAnnotation(Colum.class);
//或者可以先判断有无该注解
field.isAnnotationPresent(Colum.class);
//将该属性通过注解配置好的中文含义取出来放到集合中
columNames.add(colum.name());
}

//打印集合
columNames.forEach((columName) -> System.out.println(columName));
}