一换淆、元注解
@interface是一種自定義的注解類型,他可以由四種元注解修飾真屯,分別是@Target脸候、@Retention、@Documented绑蔫、@Inherited运沦。
//如何使用元注解修飾創(chuàng)建的自定義注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyFirstAnnotation {
String name() default "author";
int age();
}
在其他類中使用自定義的注解:
/*@interface MyFirstAnnotation后的MyFirstAnnotation就是自定義注解的名字。
*在創(chuàng)建自定義注解時(shí)如果沒(méi)有設(shè)置默認(rèn)值的話就必須進(jìn)行賦值操作(由于age沒(méi)有默認(rèn)值配深,所以這邊必須賦值)
*/
@MyFirstAnnotation(age = 23)
public class Demo {
}
1携添、@Target
@target:主要用來(lái)設(shè)置注解的使用范圍
public enum ElementType {
//主要用于修飾類,接口篓叶,枚舉類型等
TYPE,
//修飾作用域
FIELD,
//修飾方法
METHOD,
//修飾參數(shù)
PARAMETER,
//修飾構(gòu)造函數(shù)
CONSTRUCTOR,
//修飾局部變量
LOCAL_VARIABLE,
//用于描述包
PACKAGE,
}
2烈掠、@Retention
@Retention:主要用于控制注解的生命周期,主要有三種類型:SOURCE缸托、CLASS左敌、RUNTIME。
public enum RetentionPolicy {
//源碼級(jí)別俐镐,只存在于源碼中矫限,用于與編譯器交互進(jìn)行代碼檢測(cè)(@Override,@SuppressWarings等)
//一般用于生成源碼級(jí)別的框架
SOURCE,
//字節(jié)碼級(jí)別,注解的信息會(huì)被保留在class文件中,但是不會(huì)存在與JVM中
CLASS,
//運(yùn)行時(shí)級(jí)別叼风,存在于JVM虛擬機(jī)中取董,主要用于反射來(lái)獲取相關(guān)的信息。一般用于生成運(yùn)行級(jí)別的框架
RUNTIME;
}
3咬扇、@Documented與@Inherited
@Documented:被修飾的注解會(huì)生成到j(luò)avadoc中
@Inherited:如果父類被注解修飾的話甲葬,子類會(huì)繼承這個(gè)注釋
public class Demo {
@MyFirstAnnotation(age = 23)
private static class Father {
}
private static class Child extends Father{
}
public static void main(String... args){
Father child=new Child();
//isAnnotationPresent可以用來(lái)判斷當(dāng)前類是否使用這個(gè)注解
if (child.getClass().isAnnotationPresent(MyFirstAnnotation.class)){
System.out.println("isAnnotationPresent:true");
}
}
}
二、反射機(jī)制運(yùn)行處理的注解
自定義的注解主要有兩種形式懈贺,一種是通過(guò)反射來(lái)獲取對(duì)象相應(yīng)的注釋经窖,另一種是通過(guò)注釋處理器在編譯時(shí)處理注解。
運(yùn)用反射機(jī)制去獲取注釋對(duì)象梭灿,要求注釋必須設(shè)置為@Retention(RetentionPolicy.RUNTIME)画侣,即在JVM運(yùn)行時(shí)也要保存對(duì)應(yīng)的注釋。
首先定義三個(gè)注釋堡妒,更別用于修飾類配乱,方法,成員變量
@Target(ElementType.TYPE)//用于修飾類
@Retention(RetentionPolicy.RUNTIME)
public @interface MyFirstAnnotation {
String name() default "author";
}
@Target(ElementType.FIELD)//用于修飾成員變量
@Retention(RetentionPolicy.RUNTIME)
public @interface MySecondAnnotation {
String gender() default "male";
}
@Target(ElementType.METHOD)//用于修飾方法
@Retention(RetentionPolicy.RUNTIME)
public @interface MyThirdAnnotation {
String likeFood() ;
String work() ;
}
然后在創(chuàng)建的保存用戶信息的類當(dāng)中使用新建的注釋
@MyFirstAnnotation(name = "lilei")
public class User {
@MySecondAnnotation(gender = "male")
public String userInfo = "";
@MyThirdAnnotation(likeFood = "milk", work = "teacher")
public void getOtherInformation() {
}
}
因?yàn)镃lass皮迟,Method搬泥,F(xiàn)ield都實(shí)現(xiàn)了AnnotatedElement接口,所以可以調(diào)用isAnnotationPresent()方法去判斷當(dāng)前對(duì)象是否被對(duì)應(yīng)的注釋給修飾伏尼,也可以通過(guò)getAnnotation()獲取對(duì)應(yīng)注釋的實(shí)例忿檩。
void getUserInfo(String className) {
Class c =Class.forName(className);
//通過(guò)isAnnotationPresent()方法判斷是否被MyFirstAnnotation給修飾
if (c.isAnnotationPresent(MyFirstAnnotation.class)) {
//通過(guò)getAnnotation()獲取對(duì)應(yīng)注釋的實(shí)例,接著通過(guò)調(diào)用對(duì)應(yīng)的方法就可以獲取name值爆阶。
MyFirstAnnotation annotation = (MyFirstAnnotation) c.getAnnotation(MyFirstAnnotation.class);
name.setText(annotation.name());
}
//field和method的獲取對(duì)應(yīng)注釋實(shí)例的過(guò)程與Class是一樣的
for (Field field : c.getDeclaredFields()) {
if (field.isAnnotationPresent(MySecondAnnotation.class)) {
MySecondAnnotation annotation = field.getAnnotation(MySecondAnnotation.class);
sex.setText(annotation.gender());
}
}
for (Method method : c.getMethods()) {
MyThirdAnnotation annotation = method.getAnnotation(MyThirdAnnotation.class);
if (annotation != null) {
like.setText(annotation.likeFood());
work.setText(annotation.work());
}
}
}
除了上面介紹的isAnnotationPresent()和getAnnotation(),AnnotatedElement接口中還有g(shù)etAnnotations()和getDeclaredAnnotations()兩個(gè)方法燥透,分別用于獲取該對(duì)象的所有注釋和該對(duì)象上直接存在的所有注釋(不包括父類中Inherited修飾的注解)。
三辨图、注釋處理器來(lái)處理注釋
注解處理器(Annotation Processor)是javac的一個(gè)工具班套,它用來(lái)在編譯時(shí)掃描和處理注解(由于是在編譯期間就開(kāi)始處理注釋,因此注釋的生命周期@Retention(***)可以設(shè)置為三種中的任意一種)故河。注解處理器的主要作用就是解析注解吱韭,獲取注解相對(duì)應(yīng)的值,然后以此為基礎(chǔ)進(jìn)行邏輯操作忧勿。
使用注釋處理器首先需要?jiǎng)?chuàng)建一個(gè)首先創(chuàng)建一個(gè)Java Library,并且引用auto-Service的庫(kù)杉女。
compile 'com.google.auto.service:auto-service:1.0-rc3'
然后創(chuàng)建注釋處理器的類,需要繼承AbstractProcessor鸳吸,
//autoService主要用于向javac注冊(cè)我們自定義的注釋解釋器的(當(dāng)然我們也可以自己定義注釋解釋器)
@AutoService(Processor.class)
//用于確定我們使用的java版本,在這里我們?cè)O(shè)置為java7(使用這個(gè)和下面兩個(gè)注解可以代替
//getSupportedAnnotationTypes()和getSupportedSourceVersion()方法)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
//用于指定相應(yīng)的注釋(需要完整的包名)
@SupportedAnnotationTypes("com.example.MyFourthAnnotation")
public class MyProcessor extends AbstractProcessor {
//編譯期間速勇,init()會(huì)自動(dòng)被注解處理工具調(diào)用晌砾,并傳入ProcessingEnviroment參數(shù),
//通過(guò)該參數(shù)可以獲取到很多有用的工具類: Elements , Types , Filer 等等
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv)烦磁;
}
//Annotation Processor掃描出的結(jié)果會(huì)存儲(chǔ)進(jìn)roundEnv中养匈,可以在這里獲取到注解內(nèi)容哼勇,編寫你的操作邏輯(比如生成java文件)
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
//用于指定對(duì)應(yīng)的注釋,返回一個(gè)String集合(可被上面的注釋代替)
@Override
public Set<String> getSupportedAnnotationTypes() {
return super.getSupportedAnnotationTypes();
}
//用來(lái)指定你使用的Java版本(可被上面的注釋代替)
@Override
public SourceVersion getSupportedSourceVersion() {
return super.getSupportedSourceVersion();
}
}
在init()方法中可以獲取到許多工具類,通過(guò)這些工具類呕乎,我們可以打印日志积担,進(jìn)行java文件的創(chuàng)建與寫入(不能在原有的java文件上進(jìn)行修改),或者使用Elements進(jìn)行獲取包名等操作猬仁。
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
//用于創(chuàng)建java文件帝璧,并寫入它
Filer mFiler = processingEnv.getFiler();
//一些實(shí)用的方法
Elements mElements = processingEnv.getElementUtils();
//用于打印具體的消息(通過(guò)messager來(lái)打印消息)
Messager mMessager = processingEnv.getMessager();
}
在process()中的roundEnv可以獲取到所有使用MyFourthAnnotation注釋的元素(實(shí)現(xiàn)了AnnotatedElement接口),因此可以輕松拿到注釋的實(shí)例湿刽。然后就可以根據(jù)需求做對(duì)應(yīng)的操作的烁。這邊只是做了簡(jiǎn)單的打印工作。
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//獲取所有使用MyFourthAnnotation注釋的元素(這邊的元素可以是類诈闺,方法渴庆,變量等等)。
for (Element element : roundEnv.getElementsAnnotatedWith(MyFourthAnnotation.class)) {
MyFourthAnnotation annotation = element.getAnnotation(MyFourthAnnotation.class);
int id = annotation.value();
//設(shè)置為Diagnostic.Kind.ERROR時(shí)雅镊,會(huì)編譯不過(guò)去襟雷,報(bào)錯(cuò)
mMessager.printMessage(Diagnostic.Kind.NOTE, "MyFourthAnnotation---->value:" + id);
}
return false;
}
在app編譯的過(guò)程中會(huì)打印下面的log.
注釋處理器的功能非常強(qiáng)大,可以通過(guò)跟javapoet庫(kù)配合使用生成實(shí)用的Java類仁烹。