注解的定義
在Java中,要聲明一個注解很簡單,只要你會使用接口,你就一定會使用注解,以下是一個簡單的注解的例子:
public @interface Table{
// add code to yourself
}
聰明的你一定會發現,把接口變成注解只需要再interface前增加一個@符號就可以了.
Java中的元注解
@Target
Target注解聲明了Annotation所修飾對象的范圍,它可攜帶的參數可枚舉如下:
public enum ElementType {
/**類|接口|枚舉類型
*Class, interface (including annotation type), or enum declaration */
TYPE,
/**成員變量 包括枚舉常量
*Field declaration (includes enum constants)*/
FIELD,
/** 方法聲明 Method declaration */
METHOD,
/** 形參聲明 Formal parameter declaration */
PARAMETER,
/** 構造函數 Constructor declaration */
CONSTRUCTOR,
/** 局部變量 Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** 包 Package declaration */
PACKAGE,
/**
* Type parameter declaration *
* @since 1.8 java 1.8 以上新增支持
* @hide 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
* @since 1.8 java 1.8 以上新增支持
* @hide 1.8
*/
TYPE_USE
}
@Retention
該注解主要是聲明Annotation被保留的時間線,有些Annotation值保留在源碼中,有些Annotation 會被編譯到class文件中,而有些Annotation會被JVM所讀取,這些都是@Retention在起控制作用。
public enum RetentionPolicy {
/**存在于源碼中,注解將被編譯器丟棄*/
SOURCE,
/** 注解會被編譯到CLASS文件中,注解將被編譯器記錄到類文件中,但在運行時將被VM所丟棄/
CLASS,
/**它會一直保留到VM運行時,因此它可以被反射機制所捕獲*/
RUNTIME
}
@Documented
需要被javadoc所記錄
@Inherited
該注解 聲明了某個被標注的類型是被繼承,如果一個使用Inherited的注解被用于<b>Class</b>中,那么它的子類也會繼承這個注解.
解析注解
使用以下方法可以訪問Annotation的注解信息
/**注解屬性是否是指定類型*/
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType)
/**返回指定類型的注解*/
public <A extends Annotation> A getAnnotation(Class<A> annotationType)
/**返回所有的注解(該方法將忽略繼承到的注解類型)*/
public native Annotation[] getDeclaredAnnotations()
/**返回所有的注解(該方法將包含繼承到的注解類型)*/
public Annotation[] getAnnotations()
代碼實現
package com.deity.application.annotation;
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;
/**
* 自定義注解
* Created by Deity on 2017/2/8.
*/
@SuppressWarnings("unused")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HumanAnnotation {
/**訪問權修飾符 public 或者 protect*/
String userName() default "還未起名字";
String getMyBrithday() default "2016-09-10 10:00:00";
int getMyAge() default 1;
}
package com.deity.application.annotation;
/**
* 女兒
* Created by Deity on 2017/2/8.
*/
public class Daughter {
/**起個響亮的名字*/
private String userName;
/**生日*/
private String birthday;
/**年齡*/
private int age;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
@HumanAnnotation(userName = "小寶寶",getMyBrithday = "2016年2月8日22:11:50",getMyAge = 2)
public String toString() {
String result = "這是我女兒>>>姓名:"+userName+" 年齡:"+age+" 出生日期:"+birthday;
return result;
}
}
package com.deity.application;
import com.deity.application.annotation.Daughter;
import com.deity.application.annotation.HumanAnnotation;
import org.junit.Test;
import java.lang.reflect.Method;
/***
* 自定義注解測試
*/
public class ExampleUnitTest {
@Test
public void AnnotationTest(){
Daughter daughter = new Daughter();
Method[] fields = Daughter.class.getDeclaredMethods();
for (Method field:fields){
if (field.isAnnotationPresent(HumanAnnotation.class)) {
HumanAnnotation humanAnnotation = field.getAnnotation(HumanAnnotation.class);
daughter.setAge(humanAnnotation.getMyAge());
daughter.setBirthday(humanAnnotation.getMyBrithday());
daughter.setUserName(humanAnnotation.userName());
}
}
System.out.println(daughter.toString());
}
}
實現一個編譯時注解
package com.example;
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;
/**
* 注解Demo
* Created by Deity on 2017/2/8.
*/
@SuppressWarnings("unused")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface HumanMessageAnnotation {
/**public Or protect*/
String userName() default "unknow";
String getMyBrithday() default "2016-09-10 10:00:00";
int getMyAge() default 1;
}
注解器實現:
package com.example;
import com.google.auto.service.AutoService;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.annotation.processing.Processor;
/**
* 1.繼承 AbstractProcessor
*/
//@SupportedSourceVersion(SourceVersion.RELEASE_6) 1
//@SupportedAnnotationTypes("com.example.HumanMessageAnnotation") 2
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor{
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
/**
* {@inheritDoc}
*
* @param annotations
* @param roundEnv
* @return true
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(HumanMessageAnnotation.class)) {
if (ElementKind.METHOD == element.getKind()){
ExecutableElement typeElement = (ExecutableElement ) element;
System.out.println(typeElement.getAnnotation(HumanMessageAnnotation.class).userName()+"\n"+
typeElement.getAnnotation(HumanMessageAnnotation.class).getMyBrithday()+"\n"+
typeElement.getAnnotation(HumanMessageAnnotation.class).getMyAge());
}
}
return false;
// for (Element element : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) {
// System.out.println("------------------------------");
// if (element.getKind() == ElementKind.CLASS) {
// TypeElement typeElement = (TypeElement) element;
// System.out.println(typeElement.getSimpleName());
// System.out.println(typeElement.getAnnotation(MyAnnotation.class).value());
// }
// System.out.println("------------------------------");
// }
// return false;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotations = new LinkedHashSet<>();
annotations.add(HumanMessageAnnotation.class.getCanonicalName());//基礎類的規范名稱
return annotations;
// Set<String> annotataions = new LinkedHashSet<String>();
// annotataions.add(MyAnnotation.class.getCanonicalName());
// return annotataions;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
擴展
javax.lang.model.element 中 Element 的子接口
................................................................................................................
interface ExecutableElement
表示某個類或接口的方法、構造方法或初始化程序(靜態或實例),包括注釋類型元素。
interface PackageElement
表示一個包程序元素。
interface TypeElement
表示一個類或接口程序元素。
interface TypeParameterElement
表示一般類、接口、方法或構造方法元素的形式類型參數。
interface VariableElement
表示一個字段、enum常量、方法或構造方法參數、局部變量或異常參數。
注解處理器的注冊
手動注冊注解處理器
1、在 processors 庫的 main 目錄下新建 resources 資源文件夾;
2、在 resources文件夾下建立 META-INF/services 目錄文件夾;
3、在 META-INF/services 目錄文件夾下創建 javax.annotation.processing.Processor 文件;
4、在 javax.annotation.processing.Processor 文件寫入注解處理器的全稱,包括包路徑;
使用AutoService 實現注解處理器的注冊
compile 'com.google.auto.service:auto-service:1.0-rc3'
使用APT
在工程build.gradle上,增加依賴
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' // add
在module上增加依賴
apply plugin: 'com.neenbedankt.android-apt' // add
dependencies {
//.............
compile project(':annotations')
apt project(':processor')
}
參考:
深入理解Java:注解Annotation自定義注解入門
APT(Annotation Processing Tool) 原理
APT 是一種處理注釋的工具,對Android開發者來說,市面上比較出名的就是android-apt,但是android-apt,從Android Studio 2.2開始,Google已經內置了一款APT插件,那就是annotationProcessor,目前android-apt已宣布android-apt完成它的歷史使命,并推薦大家使用官方annotationProcessor插件.