1.自定义注解的格式
通过查看源码,可以发现任何一个注解类都有如下特征:
- 注解类会被@interface标记
- 注解类的顶部会被@Documented、@Retention、@Target、@Inherited这四个注解标记,其中@Documented、@Inherited可选的,@Retention、@Target必须有
定义一个注解的格式:
例如:
@Target(value = {ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Id {
public String value() defult "";
}
抽象出来就是:
元注解
public @interface 注解名{
定义体;
}
看一下@UiThread源码:
@Documented
@Retention(CLASS)
@Target({METHOD,CONSTRUCTOR,TYPE})
public @interface UiThread {
}
2.常见元注解
定义:用来描述注解类属性的注解。
作用:主要用来描述注解类的一些属性。
常见的元注解:
① @Target:描述注解的使用范围,注解可以用到的地方
取值:
CONSTRUCTOR:构造器
FIELD:成员变量定义
LOCAL_VARIABLE:局部变量
METHOD:方法
PACKAGE:包
PARAMETER:参数
TYPE:类,接口(包括注解类型)enum声明
② @Retention:表示需要在什么级别保存该注解信息,用与描述注解的生命周期,即被描述的注解在什么范围内有效
取值:
SOURCE:SOURCE源文件中有效,及原文件保留;
CLASS:在class文件中有效,及class保留
RunTIME时有效,运行时保留
③ Documented:一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中
④ @Inherited:描述某个被标注的类型是可被继承的
3.注解参数可支持的数据类型
1、所有基本数据类型(int,float,boolean,byte,double,char,long,short);
2、String类型;
3、Class类型;
4、enum类型;
5、Annotation类型;
6、以上所有类型的数组
4.注解处理器
我们定义注解的目的就是为了使用注解,所以我们就需要创建一个用于解读注解的注解处理器。
Java使用Annotation接口来代表程序元素前面的注解,该接口是所有Annotation类型的父接口。
对于自定义的注解类我们需要关心Java在java.lang.reflect 包下新增的AnnotatedElement接口,该接口代表我们的注解可以应用的地方
该接口主要有如下几个实现类:
- Class:类定义
- Constructor:构造器定义
- Field:累的成员变量定义
- Method:类的方法定义
- Package:类的包定义
java.lang.reflect 包下主要包含一些实现反射功能的工具类,实际上,java.lang.reflect 包所有提供的反射API扩充了读取运行时Annotation信息的能力。当一个Annotation类型被定义为运行时的Annotation后,该注解才能是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement 接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取了某个类的AnnotatedElement对象之后,程序就可以调用该对象的如下四个个方法来访问Annotation信息:
- 方法1: T getAnnotation(Class annotationClass): 返回改程序元素上存在的、指定类型的注解,如果该类型注解不存在,则返回null。
- 方法2:Annotation[] getAnnotations():返回该程序元素上存在的所有注解。
- 方法3:boolean is AnnotationPresent(Class<?extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注解,存在则返回true,否则返回false.
- 方法4:Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注解。与此接口中的其他方法不同,该方法将忽略继承的注解。(如果没有注解直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。
5.自定义注解使用:
- 定义注解类
FruitCatory.java
/**
*第一步:定义注解类
* Created by dkp on 2019/6/7.
* 定义一个水果类型的注解
* 步骤:
* 1.创建类用@interface修饰
* 2.用元数据Target描述注解的使用方位
* 3.用@Retention描述该注解生效范围
* 以上是必须添加的元数据,下面是根据需求可自己配置的元数据
* @Documented 一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中
* @Inherited:描述某个被标注的类型是可被继承的
*/
@Target(value={ElementType.METHOD,ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface FruitCatory {
String value() default "";
}
FruitColor.java
/**
* Created by dkp on 2019/6/7.
* 定义一个水果颜色的注解
* 步骤:
* 1.创建类用@interface修饰
* 2.用元数据Target描述注解的使用方位
* 3.用@Retention描述该注解生效范围
* 以上是必须添加的元数据,下面是根据需求可自己配置的元数据
* @Documented 一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中
* @Inherited:描述某个被标注的类型是可被继承的
*/
@Target(value={ElementType.METHOD,ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface FruitColor {
public enum Color{BLUE,RED,YELLOW};
public Color fruitColor() default Color.RED;
}
FruitProvider.java
/**
* Created by dkp on 2019/6/7.
* 定义一个水果提供商的注解
* 步骤:
* 1.创建类用@interface修饰
* 2.用元数据Target描述注解的使用方位
* 3.用@Retention描述该注解生效范围
* 以上是必须添加的元数据,下面是根据需求可自己配置的元数据
* @Documented 一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中
* @Inherited:描述某个被标注的类型是可被继承的
*/
@Target(value={ElementType.METHOD,ElementType.CONSTRUCTOR,ElementType.FIELD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface FruitProvider {
int providerId() default 0;
String providerName() default "";
String providerAddress() default "";
}
-
在类中使用注解
package com.dkp.viewdemo.Annotation; /** * Created by dkp on 2019/6/7. * 第二步:注解使用 */ public class Fruit { @FruitCatory("苹果") private String fruitCatory; @FruitColor(fruitColor = FruitColor.Color.YELLOW) private String fruitColor; @FruitProvider(providerId = 1,providerName = "烟台富士",providerAddress = "山东省烟台市富士苹果园") private String providerdes; public String getFruitCatory() { return fruitCatory; } public void setFruitCatory(String fruitCatory) { this.fruitCatory = fruitCatory; } public String getFruitColor() { return fruitColor; } public void setFruitColor(String fruitColor) { this.fruitColor = fruitColor; } public String getProviderdes() { return providerdes; } public void setProviderdes(String providerdes) { this.providerdes = providerdes; }
}
-
定义注解解析器
/** * Created by dkp on 2019/6/7. * 定义一个注解处理器,解读我们定义的注解 */ public class AnnotationProcessor { public static void getFruitInfo(Class<?> clazz){ String strFruitName=" 水果名称:"; String strFruitColor=" 水果颜色:"; String strFruitProvicer="供应商信息:"; Field[] fields = clazz.getDeclaredFields(); for (Field field :fields){ //从打印信息可以看到getDeclaredFields返回的是clazz类中用到注解的成员变量的集合。 System.out.println("field name ="+field.getName()+",fields.DeclaringClass = "+field.getDeclaringClass()); } for(Field field :fields){ //判断该程序元素上是否包含指定类型的注解 if(field.isAnnotationPresent(FruitCatory.class)){ FruitCatory fruitName = (FruitCatory) field.getAnnotation(FruitCatory.class); strFruitName=strFruitName+fruitName.value(); System.out.println(strFruitName+"do something ..."); } else if(field.isAnnotationPresent(FruitColor.class)){ FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class); strFruitColor=strFruitColor+fruitColor.fruitColor().toString(); System.out.println(strFruitColor+"do something ..."); } else if(field.isAnnotationPresent(FruitProvider.class)){ FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class); strFruitProvicer=" 供应商编号:"+fruitProvider.providerId()+" 供应商名称:"+fruitProvider.providerName()+" 供应商地址:"+fruitProvider.providerAddress(); System.out.println(strFruitProvicer+"do something ..."); } } } }
写个测试程序试验一下:
public class AnnotationTest {
public static void main(String[]arg){
AnnotationProcessor.getFruitInfo(Fruit.class);
}
}
打印结果:
field name =fruitCatory,fields.DeclaringClass = class com.dkp.viewdemo.Annotation.Fruit
field name =fruitColor,fields.DeclaringClass = class com.dkp.viewdemo.Annotation.Fruit
field name =providerdes,fields.DeclaringClass = class com.dkp.viewdemo.Annotation.Fruit
水果名称:苹果do something ...
水果颜色:YELLOWdo something ...
供应商编号:1 供应商名称:烟台富士 供应商地址:山东省烟台市富士苹果园do something ...
Process finished with exit code 0
至此,自定义注解完成。
如果对注解概念不明白的话可以看下这篇文章: