在很多框架中都使用了自定義注解,之前在項(xiàng)目中也用到了自定義注解來對代碼做一些解耦,今天就給大家介紹下自定義注解的使用方法,對于自定義注解其實(shí)很簡單,大家只要搞清楚如何定義自定義注解和如何獲取定義的自定義注解內(nèi)容就基本掌握了自定義注解,后續(xù)就可以在自己的項(xiàng)目中去使用自定義注解完成一些功能。
1. 元注解
JDK 1.5開始jdk就定義了元注解,用來定義其他的自定義注解,目前提供的元注解主要有4個(gè):
- @Target
- @Retention
- @Documented
- .@Inherited
@Target
@Target說明了Annotation所修飾的對象范圍:Annotation可被用于 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構(gòu)造方法、成員變量、枚舉值)、方法參數(shù)和本地變量(如循環(huán)變量、catch參數(shù))。
取值(ElementType)有:
- 1.CONSTRUCTOR:用于描述構(gòu)造器
- 2.FIELD:用于描述域
- 3.LOCAL_VARIABLE:用于描述局部變量
- 4.METHOD:用于描述方法
- 5.PACKAGE:用于描述包
- 6.PARAMETER:用于描述參數(shù)
- 7.TYPE:用于描述類、接口(包括注解類型) 或enum聲明
我們在日常自定義注解中常用的類型有FIELD、METHOD、PARAMETER、TYPE。
@Retention
@Retention定義了該Annotation被保留的時(shí)間長短。
取值(RetentionPoicy)有:
- 1.SOURCE:在源文件中有效(即源文件保留)
- 2.CLASS:在class文件中有效(即class保留)
- 3.RUNTIME:在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留)
我們在自定義注解用用的比較多的自然是RUNTIME了,這樣保證注解在運(yùn)行時(shí)是有效的,我們在其他框架中遇到的也大部分都是RUNTIME的。
@Documented
@Documented用于描述其它類型的annotation應(yīng)該被作為被標(biāo)注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個(gè)標(biāo)記注解,沒有成員。
@Inherited
@Inherited 元注解是一個(gè)標(biāo)記注解,@Inherited闡述了某個(gè)被標(biāo)注的類型是被繼承的。如果一個(gè)使用了@Inherited修飾的annotation類型被用于一個(gè)class,則這個(gè)annotation將被用于該class的子類。
2.自定義注解
使用@interface自定義注解時(shí),自動(dòng)繼承了java.lang.annotation.Annotation接口,由編譯程序自動(dòng)完成其他細(xì)節(jié)。在定義注解時(shí),不能繼承其他的注解或接口。@interface用來聲明一個(gè)注解,其中的每一個(gè)方法實(shí)際上是聲明了一個(gè)配置參數(shù)。方法的名稱就是參數(shù)的名稱,返回值類型就是參數(shù)的類型(返回值類型只能是基本類型、Class、String、enum)。可以通過default來聲明參數(shù)的默認(rèn)值。
注解參數(shù)支持的類型如下:
- 1.所有基本數(shù)據(jù)類型(int,float,boolean,byte,double,char,long,short)
- 2.String類型
- 3.Class類型
- 4.enum類型
- 5.Annotation類型
- 6.以上所有類型的數(shù)組
下面我們寫個(gè)例子,定義幾個(gè)自定義注解,并獲取這些自定義注解中的值。
定義了一個(gè)可以作用于類、接口、枚舉上的注解MyType。
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @Author: feiweiwei
* @Description:
* @Created Date: 16:10 17/9/22.
* @Modify by:
*/
@Documented
@Target({ ElementType.TYPE})
@Retention(RUNTIME)
public @interface MyType {
String value() default "";
String className() default "";
}
定義了一個(gè)可以作用于類的方法上的注解MyMethod。
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @Author: feiweiwei
* @Description:
* @Created Date: 16:12 17/9/22.
* @Modify by:
*/
@Documented
@Target({ ElementType.METHOD})
@Retention(RUNTIME)
public @interface MyMethod {
String value() default "";
String methodName() default "";
}
定義了一個(gè)可以作用于類內(nèi)部變量的注解MyField。
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @Author: feiweiwei
* @Description:
* @Created Date: 16:07 17/9/22.
* @Modify by:
*/
@Documented
@Target({ ElementType.FIELD})
@Retention(RUNTIME)
public @interface MyField {
String value() default "";
String name() default "";
String type() default"String";
}
下面我們寫個(gè)例子,定義一個(gè)類使用這些注解。
import com.monkey01.annotation.MyField;
import com.monkey01.annotation.MyMethod;
import com.monkey01.annotation.MyType;
/**
* @Author: feiweiwei
* @Description:
* @Created Date: 16:14 17/9/22.
* @Modify by:
*/
@MyType(value = "test", className = "TestClass")
public class TestClass {
@MyField(value = "testNum", name="num", type = "int")
private int num;
@MyField(value = "testName", name="name", type = "String")
private String name;
@MyMethod(value = "print ")
public String print(){
return "hello annotation";
}
}
從例子中可以看到,在類名上面使用了MyType的注解,在內(nèi)部申明的變量上都定義了MyField,在內(nèi)部方法上使用了MyMethod注解,java也能自動(dòng)識(shí)別這些自定義注解。
寫好了測試類后,我們下一步要做的就是如何獲取這些自定義注解上定義的屬性值,因?yàn)槎x自定義注解的目的是為了將一些公共的功能,能夠比較優(yōu)雅的與實(shí)際業(yè)務(wù)代碼隔離開。獲取注解上的屬性使用的方法實(shí)際上就是利用反射來獲取其中的注解。
下面我們寫一個(gè)類來實(shí)現(xiàn)讀取注解屬性并打印出來。
import org.reflections.Reflections;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Set;
/**
* @Author: feiweiwei
* @Description:
* @Created Date: 16:14 17/9/22.
* @Modify by:
*/
public class PrintAnnotation {
private String allAnnotation = new String();
public String printAllAnnotation(){
Set<Class<?>> clazzes = new Reflections("com.monkey01").getTypesAnnotatedWith(MyType.class);
for (Class<?> clazz : clazzes) {
printMyType(clazz);
}
System.out.println(allAnnotation);
return allAnnotation;
}
private void printMyType(Class<?> clazz) {
MyType myType = clazz.getAnnotation(MyType.class);
allAnnotation = allAnnotation + clazz.getName() + ": " + myType.value() + "-" + myType.className() + "\n";
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields){
MyField myField = field.getAnnotation(MyField.class);
if(myField != null) {
allAnnotation = allAnnotation + myField.value() + "-" + myField.name() + "-" + myField.type() + "\n";
}
}
Method[] methods = clazz.getMethods();
for(Method method : methods){
MyMethod myMethod = method.getAnnotation(MyMethod.class);
if(myMethod != null) {
allAnnotation = allAnnotation + myMethod.methodName() + "-" + myMethod.value() + "\n";
}
}
}
}
從代碼可以看到首先通過反射來獲取com.monkey01包下的使用了MyType注解的類。
Set<Class<?>> clazzes = new Reflections("com.monkey01").getTypesAnnotatedWith(MyType.class);
再循環(huán)取出使用了MyType注解的類,并打印其中的FIELD和METHOD,同樣是使用反射獲取MyField和MyMethod注解。
要獲取一個(gè)注解定義的屬性值,需要先獲取到這個(gè)注解類對象,所有的注解類對象獲取方法都是一樣的,調(diào)用class的getAnnotation方法,入?yún)檫@個(gè)注解類。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
MyType myType = clazz.getAnnotation(MyType.class);
獲取到注解類對象后就可以調(diào)用定義的方法,獲取這些方法屬性值了,是不是很簡單。
allAnnotation = allAnnotation + clazz.getName() + ": " + myType.value() + "-" + myType.className() + "\n";
要獲取Field和Method的屬性需要先通過反射獲取到獲取這個(gè)類的Field和Method。
Field[] fields = clazz.getDeclaredFields();
Method[] methods = clazz.getMethods();
對于MyField和MyMethod獲取注解屬性的方法和獲取MyType是一樣的。
總體看下來是不是很簡單,源碼已經(jīng)傳到git上,沒看懂的同學(xué)跑下例子就秒懂了,也可以在評論區(qū)留言提問。