Java注解使用、Java获取注解的属性、获取注解的字段值
一、前言
1、前面一直讲注解的基础知识,注解的定义,对于注解的使用几乎是一笔略过,本篇将着重讲讲注解的使用。 获取注解的属性,通过反射获取注解的属性值。
二、示例代码
1、定义一个注解,用于给 全局变量 field 字段 赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.haha.study.annotation.value;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
\* description: 定义一个注解,用于给 全局变量 field 字段 赋值,并使用反射取值。 <br>
\* 特别提醒: @Rentention(RetentionPolicy.RUNTIME) 时,注解才会被jvm加载,才能使用反射获取。
\* @version v1.0
\* @author w
\* @date 2018年8月1日下午2:37:40
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.FIELD)
public @interface Fields {
int sort() default 0 ;
String value() ;
}
|
2、定义一个注解 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.haha.study.annotation.value;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
\* description: 定义一个注解。
\* @version v1.0
\* @author w
\* @date 2018年8月1日下午2:41:45
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ConsAnnotation {
String[] request();
}
|
3、创建一个普通的类,使用 @ConsAnnotation、@Fields 注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package com.haha.study.annotation.value;
/**
\* description: 创建一个普通的类,使用 @ConsAnnotation、@Fields 注解。
\* @version v1.0
\* @author w
\* @date 2018年8月1日下午2:50:23
*/
@ConsAnnotation(request = { "hello","world","annotation!" })
public class User {
@Fields("中华人民共和国")
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
|
4、针对 com.haha.study.annotation.value.User 类使用注解的测试
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
|
package com.haha.study.annotation.value;
import java.lang.reflect.Field;
import java.util.Arrays;
/**
\* description: 针对 com.haha.study.annotation.value.User 类使用注解的测试
\* @version v1.0
\* @author w
\* @date 2018年8月1日下午2:37:13
*/
public class ValueTest {
public static void main(String[] args) throws Exception {
User user = new User();
// 1、 获取 User类上的注解 @ConsAnnotation
ConsAnnotation anno = user.getClass().getAnnotation(ConsAnnotation.class);
String[] arr = anno.request();
System.out.println(Arrays.toString(arr)); // [hello, world, annotation!]
// 2、 获取User类中 private String userName; 变量上的注解 @Field
Field f = user.getClass().getDeclaredField("userName");
Fields anno2 = f.getAnnotation(Fields.class);
user.setUserName(anno2.value());
System.out.println(user.getUserName()); // 中华人民共和国
}
}
|
Java的自定义注解及通过反射获取注解
一、注解基本知识
1、元注解:@Retention @Target @Document @Inherited
2、Annotation型定义为**@interface**, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口。
3、参数成员只能用public或默认(default)这两个访问权修饰
4、参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组。
5、要获取类、方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象,除此之外没有别的获取注解对象的方法
6、注解也可以没有定义成员, 不过这样注解就没啥用了,只起到标识作用
自定义注解类时, 可以指定目标 (类、方法、字段, 构造函数等) , 注解的生命周期(运行时,class文件或者源码中有效), 是否将注解包含在javadoc中及是否允许子类继承父类中的注解, 具体如下:
1、@Target 表示该注解目标,可能的 ElemenetType 参数包括:
ElemenetType.CONSTRUCTOR 构造器声明
ElemenetType.FIELD 域声明(包括 enum 实例)
ElemenetType.LOCAL_VARIABLE 局部变量声明
ElemenetType.METHOD 方法声明
ElemenetType.PACKAGE 包声明
ElemenetType.PARAMETER 参数声明
ElemenetType.TYPE 类,接口(包括注解类型)或enum声明
2、@Retention 表示该注解的生命周期,可选的 RetentionPolicy 参数包括
RetentionPolicy.SOURCE 注解将被编译器丢弃
RetentionPolicy.CLASS 注解在class文件中可用,但会被JVM丢弃
RetentionPolicy.RUNTIME JVM将在运行期也保留注释,因此可以通过反射机制读取注解的信息
3、@Documented 指示将此注解包含在 javadoc 中
4、@Inherited 指示允许子类继承父类中的注解
二、在java中的使用
2.1、定义注解
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
package annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class MyAnnotation {
/**
* 注解类
*
* @author suguoliang
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyClassAnnotation {
String uri();
String desc();
}
/**
* 构造方法注解
*
* @author suguoliang
*
*/
@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyConstructorAnnotation {
String uri();
String desc();
}
/**
* 方法注解
*
* @author suguoliang
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMethodAnnotation {
String uri();
String desc();
}
/**
* 字段注解
*
* @author suguoliang
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFieldAnnotation {
String uri();
String desc();
}
/**
* 可以同时应用到类和方法上
*
* @author 尐蘇
*
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyClassAndMethodAnnotation {
// 定义枚举
public enum EnumType {
util, entity, service, model
}
// 设置默认值
public EnumType classType() default EnumType.util;
// 数组
int[] arr() default { 3, 7, 5 };
String color() default "blue";
}
}
|
2.2基本测试
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
55
56
57
58
59
60
61
62
63
|
package annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import annotation.MyAnnotation.MyClassAndMethodAnnotation;
import annotation.MyAnnotation.MyClassAndMethodAnnotation.EnumType;
import annotation.MyAnnotation.MyClassAnnotation;
import annotation.MyAnnotation.MyConstructorAnnotation;
import annotation.MyAnnotation.MyFieldAnnotation;
import annotation.MyAnnotation.MyMethodAnnotation;
@MyClassAnnotation(desc = "The Class", uri = "com.sgl.annotation")
@MyClassAndMethodAnnotation(classType = EnumType.util)
public class TestAnnotation {
@MyFieldAnnotation(desc = "The Class Field", uri = "com.sgl.annotation#id")
private String id;
@MyConstructorAnnotation(desc = "The Class Constructor", uri = "com.sgl.annotation#constructor")
public TestAnnotation() {
}
public String getId() {
return id;
}
@MyMethodAnnotation(desc = "The Class Method", uri = "com.sgl.annotation#setId")
public void setId(String id) {
this.id = id;
}
@MyMethodAnnotation(desc = "The Class Method sayHello", uri = "com.sgl.annotation#sayHello")
public void sayHello(String name) {
if (name == null || name.equals("")) {
System.out.println("hello world!");
} else {
System.out.println(name + "\t:say hello world");
}
}
public static void main(String[] args) throws Exception {
Class<TestAnnotation> clazz = TestAnnotation.class;
// 获取类注解
MyClassAnnotation myClassAnnotation = clazz.getAnnotation(MyClassAnnotation.class);
System.out.println(myClassAnnotation.desc() + "+" + myClassAnnotation.uri());
// 获得构造方法注解
Constructor<TestAnnotation> constructors = clazz.getConstructor(new Class[] {});// 先获得构造方法对象
MyConstructorAnnotation myConstructorAnnotation = constructors.getAnnotation(MyConstructorAnnotation.class);// 拿到构造方法上面的注解实例
System.out.println(myConstructorAnnotation.desc() + "+" + myConstructorAnnotation.uri());
// 获得方法注解
Method method = clazz.getMethod("setId", new Class[] { String.class });// 获得方法对象
MyMethodAnnotation myMethodAnnotation = method.getAnnotation(MyMethodAnnotation.class);
System.out.println(myMethodAnnotation.desc() + "+" + myMethodAnnotation.uri());
// 获得字段注解
Field field = clazz.getDeclaredField("id");// 暴力获取private修饰的成员变量
MyFieldAnnotation myFieldAnnotation = field.getAnnotation(MyFieldAnnotation.class);
System.out.println(myFieldAnnotation.desc() + "+" + myFieldAnnotation.uri());
}
}
|
2.3通过反射解析
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
package annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import annotation.MyAnnotation.MyClassAndMethodAnnotation;
import annotation.MyAnnotation.MyClassAndMethodAnnotation.EnumType;
import annotation.MyAnnotation.MyClassAnnotation;
import annotation.MyAnnotation.MyMethodAnnotation;
public class ParseAnnotation {
/**
* 解析方法注解
*
* @param clazz
*/
public static <T> void parseMethod(Class<T> clazz) {
try {
T obj = clazz.newInstance();
for (Method method : clazz.getDeclaredMethods()) {
MyMethodAnnotation methodAnnotation = method.getAnnotation(MyMethodAnnotation.class);
if (methodAnnotation != null) {
// 通过反射调用带有此注解的方法
method.invoke(obj, methodAnnotation.uri());
}
MyClassAndMethodAnnotation myClassAndMethodAnnotation = method
.getAnnotation(MyClassAndMethodAnnotation.class);
if (myClassAndMethodAnnotation != null) {
if (EnumType.util.equals(myClassAndMethodAnnotation.classType())) {
System.out.println("this is a util method");
} else {
System.out.println("this is a other method");
}
System.out.println(Arrays.toString(myClassAndMethodAnnotation.arr()));// 打印数组
System.out.println(myClassAndMethodAnnotation.color());// 输出颜色
}
System.out.println("\t\t-----------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static <T> void parseType(Class<T> clazz) {
try {
MyClassAndMethodAnnotation myClassAndMethodAnnotation = clazz
.getAnnotation(MyClassAndMethodAnnotation.class);
if (myClassAndMethodAnnotation != null) {
if (EnumType.util.equals(myClassAndMethodAnnotation.classType())) {
System.out.println("this is a util class");
} else {
System.out.println("this is a other class");
}
}
MyClassAnnotation myClassAnnotation = clazz.getAnnotation(MyClassAnnotation.class);
if (myClassAnnotation != null) {
System.err.println(" class info: " + myClassAnnotation.uri());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
parseMethod(TestAnnotation.class);
parseType(TestAnnotation.class);
}
}
|
Java获取类、方法、属性上的注解
一、获取类上的注解
Java获取类上的注解有下面3个方法:
- Class.getAnnotations() 获取所有的注解,包括自己声明的以及继承的
- Class.getAnnotation(Class< A > annotationClass) 获取指定的注解,该注解可以是自己声明的,也可以是继承的
- Class.getDeclaredAnnotations() 获取自己声明的注解
下面,我们来演示一下3个方法的使用。
首先,我们定义两个注解ParentAnnotation、SubAnnotation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@Retention(RetentionPolicy.RUNTIME)
@Target(value={ElementType.TYPE})
@Documented
@Inherited //可以继承
public @interface ParentAnnotation {
}
@Target(value={ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SubAnnotation {
}
|
接下来,我们定义两个类,Parent、Sub,分别标注ParentAnnotation 注解和SubAnnotation注解
1
2
3
4
5
6
7
8
9
|
@ParentAnnotation
public class Parent {
}
@SubAnnotation
public class Sub extends Parent{
}
|
一切准备OK后,就开始测试了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class AnnotationTest {
public static void main(String[] args) {
Annotation[] allAnnos = Sub.class.getAnnotations();
Annotation[] deAnnos = Sub.class.getDeclaredAnnotations();
Annotation subAnnotation = Sub.class.getAnnotation(SubAnnotation.class);
Annotation parentAnnotation = Sub.class.getAnnotation(ParentAnnotation.class);
printAnnotation("all",allAnnos);
printAnnotation("declare",deAnnos);
printAnnotation("sub",subAnnotation);
printAnnotation("parent",parentAnnotation);
}
private static void printAnnotation(String msg,Annotation... annotations){
System.out.println("=============="+msg+"======================");
if(annotations == null){
System.out.println("Annotation is null");
}
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
System.out.println();
}
}
|
执行结果:
1
2
3
4
5
6
7
8
9
10
11
12
|
==============all======================
@com.ghs.test.annotation.ParentAnnotation()
@com.ghs.test.annotation.SubAnnotation()
==============declare======================
@com.ghs.test.annotation.SubAnnotation()
==============sub======================
@com.ghs.test.annotation.SubAnnotation()
==============parent======================
@com.ghs.test.annotation.ParentAnnotation()
|
尝试着将ParentAnnotation中的@Inherited去掉,结果如下:
1
2
3
4
5
6
7
8
9
10
11
|
==============all======================
@com.ghs.test.annotation.SubAnnotation()
==============declare======================
@com.ghs.test.annotation.SubAnnotation()
==============sub======================
@com.ghs.test.annotation.SubAnnotation()
==============parent======================
null
|
再试着将Sub类中的SubAnnotation去掉,结果如下:
1
2
3
4
5
6
7
8
9
|
==============all======================
==============declare======================
==============sub======================
null
==============parent======================
null
|
经过几番小小的测试,我们基本上可以得出下面几条结论:
- 注解只有标注了@Inherited才能被子类继承
- 当某个类没有标注任何注解时,getAnnotations()和getDeclaredAnnotations()返回空数组
- 当某个注解查询不到时,getAnnotation(Class< A > annotationType)方法返回null
二、获取方法上的注解
修改上面的ParentAnnotation与SubAnnotation,使其可以标注在方法上
@Target(value={ElementType.TYPE, ElementType.METHOD})
在Sub、Parent中分别添加一个test()方法,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@ParentAnnotation
public class Parent {
@ParentAnnotation
public void test(){
}
}
@SubAnnotation
public class Sub extends Parent{
@SubAnnotation
public void test(){
}
}
|
一切准备就绪,就可以进行测试了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
private static void testMethodAnnotation() {
Method[] methods = Sub.class.getMethods();
for (Method method : methods) {
if(method.getName().equals("test")){
Annotation[] allMAnnos = method.getAnnotations();
Annotation[] deMAnnos = method.getDeclaredAnnotations();
Annotation subMAnno = method.getAnnotation(SubAnnotation.class);
Annotation parentMAnno = method.getAnnotation(ParentAnnotation.class);
printAnnotation("allMAnnos",allMAnnos);
printAnnotation("deMAnnos",deMAnnos);
printAnnotation("subMAnno",subMAnno);
printAnnotation("parentMAnno",parentMAnno);
}
}
}
|
执行结果如下:
1
2
3
4
5
6
7
8
9
10
11
|
==============allMAnnos======================
@com.ghs.test.annotation.SubAnnotation()
==============deMAnnos======================
@com.ghs.test.annotation.SubAnnotation()
==============subMAnno======================
@com.ghs.test.annotation.SubAnnotation()
==============parentMAnno======================
null
|
尝试着删除Sub中的test方法,再次进行测试,结果如下:
1
2
3
4
5
6
7
8
9
10
11
|
==============allMAnnos======================
@com.ghs.test.annotation.ParentAnnotation()
==============deMAnnos======================
@com.ghs.test.annotation.ParentAnnotation()
==============subMAnno======================
null
==============parentMAnno======================
@com.ghs.test.annotation.ParentAnnotation()
|
经过两轮测试,可以得出以下结论:
- 子类重写的方法,注解无法被继承
- 针对方法而言,getAnnotations()与getDeclaredAnnotations()返回的结果似乎永远都是一样的。
附:针对此结论,如有不同的想法,还望不吝赐教
三、获取属性上的注解
修改上面的ParentAnnotation与SubAnnotation,使其可以标注在属性上
@Target(value={ElementType.TYPE, ElementType.METHOD,ElementTypeFIELD})
在Sub、Parent中分别添加一个name属性,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@ParentAnnotation
public class Parent {
@ParentAnnotation
public String name;
@ParentAnnotation
public void test(){
}
}
@SubAnnotation
public class Sub extends Parent{
@SubAnnotation
public String name;
@SubAnnotation
public void test(){
}
}
|
下面开始测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
private static void testFieldAnnotation() {
Field[] fields = Sub.class.getFields();
for (Field field : fields) {
Annotation[] allFAnnos= field.getAnnotations();
Annotation[] deFAnnos = field.getDeclaredAnnotations();
Annotation subFAnno = field.getAnnotation(SubAnnotation.class);
Annotation parentFAnno = field.getAnnotation(ParentAnnotation.class);
printAnnotation("allFAnnos",allFAnnos);
printAnnotation("deFAnnos",deFAnnos);
printAnnotation("subFAnno",subFAnno);
printAnnotation("parentFAnno",parentFAnno);
System.out.println("**************************************************\n");
}
}
|
执行结果如下:
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
|
==============allFAnnos======================
@com.ghs.test.annotation.SubAnnotation()
==============deFAnnos======================
@com.ghs.test.annotation.SubAnnotation()
==============subFAnno======================
@com.ghs.test.annotation.SubAnnotation()
==============parentFAnno======================
null
**************************************************
==============allFAnnos======================
@com.ghs.test.annotation.ParentAnnotation()
==============deFAnnos======================
@com.ghs.test.annotation.ParentAnnotation()
==============subFAnno======================
null
==============parentFAnno======================
@com.ghs.test.annotation.ParentAnnotation()
**************************************************
|
经过测试,我们可以得出下面的几个结论:
- 父类的属性和子类的属性互补干涉
- 针对属性而言,getAnnotations()与getDeclaredAnnotations()方法返回的结果似乎都是一样的
附:针对此结论,如有不同的想法,还望不吝赐教