目录

Java注解笔记

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、参数成员只能用基本类型byteshortcharintlongfloatdoubleboolean八种基本数据类型和StringEnumClassannotations等数据类型,以及这一些类型的数组

  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

经过几番小小的测试,我们基本上可以得出下面几条结论:

  1. 注解只有标注了@Inherited才能被子类继承
  2. 当某个类没有标注任何注解时,getAnnotations()和getDeclaredAnnotations()返回空数组
  3. 当某个注解查询不到时,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()

经过两轮测试,可以得出以下结论:

  1. 子类重写的方法,注解无法被继承
  2. 针对方法而言,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()

**************************************************

经过测试,我们可以得出下面的几个结论:

  1. 父类的属性和子类的属性互补干涉
  2. 针对属性而言,getAnnotations()与getDeclaredAnnotations()方法返回的结果似乎都是一样的 附:针对此结论,如有不同的想法,还望不吝赐教