Java自定义注解使用

最近上子路老师的spring源码课,发现部分刚入门的新同学对自定义注解这一块知识不太了解,于是写下这篇文章,希望能够解决一些同学心中的疑问

回到正文,什么是注解?

注解和class,Interface一样,是Java的一种数据类型。注解是不会直接对程序有说明影响的,你可以把它简单的理解为一种标记

怎么自定义一个注解?

自定义注解语句和定义类是一样的,只是声明关键字变成了@interface,如哦public @interface Feiyang

1 package com.feiyang.test;
2 
3 public class AnnotationTest {
4 
5 }
View Code

定义一个注解之后我们就可以使用它了,定义一个类来添加自定义注解

 1 package com.feiyang.model;
 2 
 3 import com.feiyang.annotation.FeiyangAnnotation;
 4 
 5 @FeiyangAnnotation
 6 public class Feiyang {
 7 
 8     @FeiyangAnnotation
 9     private String name;
10 
11     @FeiyangAnnotation
12     public Feiyang(String name) {
13         this.name = name;
14     }
15 
16     public String getName() {
17         return name;
18     }
19 
20     public void setName(String name) {
21         this.name = name;
22     }
23 
24 }
View Code

我们可以看到类、属性、方法和构造方法上都加上了我们定义的注解,并且程序并未报错

但是我们发现有些注解是只能在特定的地方才能使用,否则就会报错的,比如spring提供的@Component等注解只能用在类上,用在方法等其他地方是会报错的

这是因为Java为我们提供了一些源注解,可以用来定义注解的一些性质,比如@Target

 1 package com.feiyang.annotation;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Target;
 5 
 6 /**
 7  * Target注解是java提供的一种源注解,可以用来定义注解的作用域
 8  * 使用Target需要提供ElementType枚举类的值,值可以是一个也可以是多个组成的一个数组
 9  * ElementType.TYPE 定义注解可以作用在类、接口等声明上
10  * ElementType.FIELD 定义注解可以作用在属性上
11  * ElementType.METHOD 定义注解可以作用在方法上
12  * ElementType.PARAMETER 定义注解可以作用在方法参数上
13  * ElementType.CONSTRUCTOR 定义注解可以作用在构造方法上
14  * ElementType.LOCAL_VARIABLE 定义注解可以作用在局部变量上
15  * ElementType.ANNOTATION_TYPE 定义注解可以作用在注解上
16  * ElementType.PACKAGE 定义注解可以作用在包声明上
17  * ElementType.TYPE_PARAMETER jdk1.8之后添加的,笔者也不知道
18  * ElementType.TYPE_USE 同上
19  * 如果不加@Target注解,那该注解就能作用在任意地方
20  */
21 @Target({ElementType.FIELD, ElementType.METHOD})
22 public @interface FeiyangAnnotation {
23 
24 }
View Code

添加@Target限制之后就会发现原来注解在类上和构造方法上的注解编译报错,提示FeiyangAnnotation注解不能作用在这里

我们知道有些注解中是存在一些属性(例如Target)的,我们也可以给我们自定义的注解添加属性

 1 package com.feiyang.annotation;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Target;
 5 
 6 /**
 7  * Target注解是java提供的一种源注解,可以用来定义注解的作用域
 8  * 使用Target需要提供ElementType枚举类的值,值可以是一个也可以是多个组成的一个数组
 9  * ElementType.TYPE 定义注解可以作用在类、接口等声明上
10  * ElementType.FIELD 定义注解可以作用在属性上
11  * ElementType.METHOD 定义注解可以作用在方法上
12  * ElementType.PARAMETER 定义注解可以作用在方法参数上
13  * ElementType.CONSTRUCTOR 定义注解可以作用在构造方法上
14  * ElementType.LOCAL_VARIABLE 定义注解可以作用在局部变量上
15  * ElementType.ANNOTATION_TYPE 定义注解可以作用在注解上
16  * ElementType.PACKAGE 定义注解可以作用在包声明上
17  * ElementType.TYPE_PARAMETER jdk1.8之后添加的,笔者也不知道
18  * ElementType.TYPE_USE 同上
19  * 如果不加@Target注解,那该注解就能作用在任意地方
20  */
21 @Target({ElementType.FIELD, ElementType.METHOD})
22 public @interface FeiyangAnnotation {
23 
24     /**
25      * 我们可以在注解当中添加方法,这样就能丰富我们注解的功能了
26      * 方法名称就是使用该注解时的属性,返回值类型就是使用是时的属性值的类型,返回值就是使用时的属性值
27      * 例如在某个属性上使用该注解时@FeiyangAnnonation(value = "value属性值", name = "name属性值")
28      * 由于我们定义了一个方法名叫value,使用时就需要提供一个value属性
29      * 我们定义的返回值类型为Stirng,value属性的类型就要是String类型
30      * value属性的值是我们解析该注解的时候用到的
31      *
32      * @return
33      */
34     public String value();
35 
36     /**
37      * 上面定义的方法在使用时是一定要提供value属性及其属性值的
38      * 如果想在使用时不用填写属性,可以给方法加一个默认值(default)
39      *
40      * @return
41      */
42     public String name() default "";
43 
44 }
View Code

由于value方法没有默认值,如果不为注解提供value属性值就会报错,这里我们为注解添加属性值

 

有个小知识点,如果注解里面有个方法名称为value,并且使用时你只提供value属性,这时候是不需要写属性名的,可以直接写属性值

如果注解中没有value方法或者需要提供多个属性时,则必须填写"属性 = 属性值"

例如@FeiyangAnnotation("feiyang field value")等于@FeiyangAnnotation(value = "feiyang field value")

而@FeiyangAnnotation(value = "feiyang method value", name = "feiyang method name")不能简写成@FeiyangAnnotation("feiyang method value", name = "feiyang method name")

前面说过,注解是不能直接影响程序的,就像人名一样,我叫肥羊,你也可以叫肥羊,大家都可以叫肥羊,肥羊本身没有任何意义只是一个代号,一种表示

但是我们可以通过解析这种标识去赋予它特殊的意义,就像子路老师通过为大家授业解惑,使子路这个名字有了特殊意义,大家一听到子路老师的名字就浮现出大神、帅气、车快,人快、3秒等词

话不多说,上代码

 1 package com.feiyang.test;
 2 
 3 import com.feiyang.annotation.FeiyangAnnotation;
 4 import com.feiyang.model.Feiyang;
 5 
 6 import java.lang.reflect.Field;
 7 
 8 public class AnnotationTest {
 9 
10     public static void main(String[] args) {
11         // 解析注解分为以下几个步骤
12         // 1、获取Class对象
13         // 获取对象可以通过类.class获取,也可以通过对象.getClass方法获取,视具体情况而定
14         Class<Feiyang> clazz = Feiyang.class;
15         // 2、获取注解作用的地方,如属性,方法等。如果注解就是作用在类中,可以跳过这一步
16         // FeiyangAnnotation只能加在属性或者方法上,以属性为例,获取类中所有属性,遍历执行第三步
17         Field[] fields = clazz.getDeclaredFields();
18         for (Field field : fields) {
19             // 3、判断目标是否添加了该注解
20             if (field.isAnnotationPresent(FeiyangAnnotation.class)) {
21                 // 如果添加了注解,我们就可以操作添加了注解的元素或者解析注解了
22                 // spring的@Autowired注解就是这个原理,当解析到类中的某个属性添加了@Autowired注解时
23                 // spring就会获取这个属性的类型,然后去容器中找对应的bean,如果找到了就注入给这个属性
24 
25                 // 4、获取注解对象,解析属性
26                 // 添加注解的话我们就可以获取到这个注解对象了
27                 FeiyangAnnotation annotation = field.getAnnotation(FeiyangAnnotation.class);
28                 // 获取到这个注解对象之后我们就可以拿到这个注解的属性值了
29                 // 前面我们使用该注解提供的属性值就相当于注解方法的返回值
30                 // 如果我们方法有默认值而我们又没有提供对应的属性,我们调用方法获取到的就是默认值
31                 String value = annotation.value();
32                 String name = annotation.name();
33                 // 获取到属性值之后我们就可以通过判断属性值确认逻辑了,这里笔者就不演示逻辑了,笔者把属性值打印一下给大家看一下
34                 System.out.println(value + "
" + name);
35             }
36         }
37     }
38 
39 }
View Code

运行代码可以看到控制台并没有打印任何信息

 这是什么情况?难道笔者前面说的都是错的吗?不要着急,这其实是注解缺少了一个值的原因

我们前面说过Target源注解,Java还提供了另外一个源注解@Retention标记注解存在的时间,我们需要添加这个源注解才能使代码达到我们的预期

 通过上面的注释我们可以发现是没有加@Retention注解的原因导致执行时注解就被丢弃了,现在我们加上这个注解并定义为RUNTIME,再次执行程序,可以看到打印出了我们想要的结果

 剩下的对方法上的注解的解析大家可以自己去试一下

原文地址:https://www.cnblogs.com/big-feiyang/p/11661442.html