亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺析Java自定義注解的用法

 更新時間:2023年03月21日 10:30:58   作者:_小馬快跑_  
注解為我們在代碼中添加信息提供一種形式化的方法,使我們可以在源碼、編譯時、運行時非常方便的使用這些數(shù)據(jù)。本文主要為大家介紹了Java自定義注解的用法,希望對大家有所幫助

注解

注解為我們在代碼中添加信息提供一種形式化的方法,使我們可以在源碼、編譯時、運行時非常方便的使用這些數(shù)據(jù)。

注解是在JAVA SE5中引入的,注解讓代碼更干凈易讀并且可以實現(xiàn)編譯期類型檢查等。當(dāng)創(chuàng)建描述性質(zhì)的類或接口時,如果有重復(fù)性的工作,就可以考慮使用注解來簡化或自動化該過程。我們可以讓注解保存在源代碼中,并且利用Annotation API處理注解,得到我們想要的數(shù)據(jù)并加以處理,注解的使用比較簡單,JAVA SE5內(nèi)置了3種:

  • @Override 表示當(dāng)前類中的方法將覆蓋父類中的方法,如果不寫也不會有錯,但是@Override可以起到檢查作用,如方法名拼寫錯誤,編譯器就會報警告信息。
  • @Deprecated 表示被標(biāo)注的方法已經(jīng)被廢棄了,如果使用編譯器會發(fā)出警告信息。
  • @SuppressWarnings 關(guān)閉不當(dāng)?shù)木幾g器警告信息。除非你確定編譯器的警告信息是錯誤的,否則最好不要使用這個注解。

定義注解

先來看內(nèi)置注解@Override是怎么被定義的,它位于package java.lang之下:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

@Target、@Retention稱為元注解,元注解負責(zé)注解其他的注釋,如:@Target定義聲明的注解的作用域(作用在類上還是方法上),@Retention定義注解在哪個級別可用,在源代碼中(SOURCE)、類文件中(CLASS)、還是運行時(RUNTIME)。除了@Target、@Retention還有@Documented及@Inherited,下面用一個表格來分別列出他們各自的作用:

元注解作用
@Target表示注解作用在什么地方,CONSTRUCTOR 聲明在構(gòu)造器、FIELD 域聲明、METHOD 方法聲明、PACKAGE 包聲明、TYPE 類、接口或者enum聲明、PARAMETER參數(shù)聲明、LOCAL_VALABLE局部變量聲明
@Retention表示在什么級別保存注解信息,SOURCE注解在編譯器編譯時丟棄、CLASS注解在編譯之后的class文件中存在,但會被VM丟棄、RUNTIME VM將在運行期也保留注解,因此可以用反射讀取注解的信息
@Documented將此注解包含在JavaDoc中
@Inherited允許子類繼承父類中的注解

@Retention作用范圍如下圖所示:

注解處理器

首先來自定義一個注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationInfo {

    String[] value();

    int requestCode() default 0;
}
  • 注解中定義的方法沒有參數(shù),且返回類型僅限于原始類型,字符串,枚舉,注解或以上類型的集合
  • 注解中定義的方法可以有默認(rèn)值

運行時解析注解

@Target(ElementType.METHOD)指明了我們的注解是作用在方法上的

@Retention(RetentionPolicy.RUNTIME)表示注解在程序運行時期也會存在,即注解信息也會加載到虛擬機VM中,所以可以通過反射來獲取注解的相關(guān)信息:

編寫一個類,聲明方法,并在方法上聲明我們的自定義注解,如下:

public class AnnotationExample {

    /**
     * 注解模擬請求權(quán)限
     */
    @AnnotationInfo(value = {"android.permission.CALL_PHONE", "android.permission.CAMERA"}, requestCode = 10)
    public void requestPermission() {
        //其他邏輯
    }
}

接著來編寫一個運行時解析注解的Java類:AnnotationRuntimeProcessor.java

public class AnnotationRuntimeProcessor {

    public static void main(String[] args) {
        try {
            //獲取AnnotationExample的Class對象
            Class<?> cls = Class.forName("com.javastudy.Annotation.AnnotationExample");
            //獲取AnnotationExample類中的方法
            Method[] methods = cls.getDeclaredMethods();
            for (Method method : methods) {
                //過濾不含自定義注解AnnotationInfo的方法
                boolean isHasAnnotation = method.isAnnotationPresent(AnnotationInfo.class);
                if (isHasAnnotation) {
                    method.setAccessible(true);
                    //獲取方法上的注解
                    AnnotationInfo aInfo = method.getAnnotation(AnnotationInfo.class);
                    if (aInfo == null) return;
                    //解析注解上對應(yīng)的信息
                    String[] permissions = aInfo.value();
                    System.out.println("value: " + Arrays.toString(permissions));

                    int requestCode = aInfo.requestCode();
                    System.out.println("requestCode: " + requestCode);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上面的邏輯很簡單,反射拿到有注解對應(yīng)類的Class對象,篩選含有注解的方法,最后獲取方法上的注解并解析,運行結(jié)果如下:

value: [android.permission.CALL_PHONE, android.permission.CAMERA]
requestCode: 10

編譯時解析注解

AbstractProcessor是javax下的API,java和javax都是Java的API(Application Programming Interface)包,java是核心包,javax的x是extension的意思,也就是擴展包。一般繼承AbstractProcessor需要實現(xiàn)下面的幾個方法:

public class ProcessorExample extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        //processingEnvironment提供各種工具類  如Elements Filer Types SourceVersion等
        super.init(processingEnvironment);
    }

    /**
     * 掃描 評估和處理注解代碼  生成Java代碼
     *
     * @param set      注解類型
     * @param roundEnvironment 有關(guān)當(dāng)前和以前的信息環(huán)境 查詢出包含特定注解的被注解元素
     * @return 返回true 表示注解已聲明 后續(xù)Processor不會再處理  false表示后續(xù)Processor會處理他們
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        return false;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return super.getSupportedSourceVersion();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }
}
  • init(ProcessingEnvironment env): 每一個注解處理器類都必須有一個空的構(gòu)造函數(shù)。然而,這里有一個特殊的init()方法,它會被注解處理工具調(diào)用,并輸入ProcessingEnviroment參數(shù)。ProcessingEnviroment提供很多有用的工具類Elements, Types和Filer。后面我們將看到詳細的內(nèi)容。
  • process(Set (? extends TypeElement) annotations, RoundEnvironment env): 這相當(dāng)于每個處理器的主函數(shù)main()。你在這里寫你的掃描、評估和處理注解的代碼,以及生成Java文件。輸入?yún)?shù)RoundEnviroment,可以讓你查詢出包含特定注解的被注解元素。后面我們將看到詳細的內(nèi)容。
  • getSupportedAnnotationTypes(): 這里你必須指定,這個注解處理器是注冊給哪個注解的。注意,它的返回值是一個字符串的集合,包含本處理器想要處理的注解類型的合法全稱。換句話說,你在這里定義你的注解處理器注冊到哪些注解上。
  • getSupportedSourceVersion(): 用來指定你使用的Java版本。通常這里返回SourceVersion.latestSupported()。然而,如果你有足夠的理由只支持Java 6的話,你也可以返回SourceVersion.RELEASE_6。推薦使用前者。

下面來看一個具體的例子,我們在新建android的普通model和library工程是沒有javax的,所以我們需要新建一個java工程,先來看下整個包結(jié)構(gòu):

首先先定義了注解:

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface CompileAnnotation {

    int value() default 0;
}

可見我們的注解是定義在變量FIELD上的,接著來編寫我們的解析器:

@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("com.suyun.aopermission.annotation.CompileAnnotation")
public class AnnotationCompileProcessor extends AbstractProcessor {

    private Messager messager;
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        //processingEnvironment提供各種工具類  如Elements Filer Types SourceVersion等
        super.init(processingEnvironment);
        messager = processingEnv.getMessager();
        filer = processingEnv.getFiler();
    }

    /**
     * 掃描 評估和處理注解代碼  生成Java代碼
     *
     * @param annotations      注解類型
     * @param roundEnvironment 有關(guān)當(dāng)前和以前的信息環(huán)境 查詢出包含特定注解的被注解元素
     * @return 返回true 表示注解已聲明 后續(xù)Processor不會再處理  false表示后續(xù)Processor會處理他們
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {

        messager.printMessage(Diagnostic.Kind.NOTE, "----------start----------");

        for (TypeElement annotation : annotations) {
            Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(annotation);
            for (Element element : elements) {
                if (element.getKind() != ElementKind.FIELD) {
                    messager.printMessage(Diagnostic.Kind.ERROR, "Only FIELD can be annotated with AnnotationInfo");
                    return true;
                }
                //獲取注解
                CompileAnnotation annotationInfo = element.getAnnotation(CompileAnnotation.class);
                //獲取注解中的值
                int value = annotationInfo.value();
                messager.printMessage(Diagnostic.Kind.NOTE, "value: " + value);
            }
        }
        return true;
    }

}

一個簡單的注解解析器就寫完了,看代碼知道我們只是簡單的把注解里的值打印出來,接下來要做的就是把我們的@CompileAnnotation注解定義到我們類中的變量FILED上了,直接上代碼:

public class MainActivity extends AppCompatActivity {

    @CompileAnnotation(value = 200)
    private TextView tv_text;
    
    //....省略其他代碼....
}

OK,接下來就是見證奇跡的時刻了,當(dāng)我們迫不及待的按下編譯按鈕后,發(fā)現(xiàn)什么都沒有發(fā)生,注解里的信息并沒有打印出來,what fu**!!!不要方,其實還少一步操作,我們只是定義了注解解析器,但是并沒有把解析器注冊到j(luò)avac中,怎么注冊呢,在main目錄下新建resources->META-INF->services->javax.annotation.processing.Processor文件,文件里指定解析器的全路徑,我的全路徑是:

com.suyun.aopermission.processor.AnnotationCompileProcessor

寫好之后的目錄如下:

接著再來編譯一下,這次有了結(jié)果:

注: ----------start----------
注: value: 200
注: ----------start----------

成功了,如果覺得上述的配置比較繁瑣的話,可以選擇使用Google開發(fā)的service庫來代替上述配置,在build.gradle中配置:

compile 'com.google.auto.service:auto-service:1.0-rc2'

然后我們的解析器中這樣寫:

@AutoService(Processor.class)
public class AnnotationCompileProcessor extends AbstractProcessor {
//....其他邏輯....
}

沒錯,我們在注解解析器里又定義了@AutoService(Processor.class)注解,這樣和上述配置是一樣的效果

自動生成.class代碼

編譯時期我們可以根據(jù)需要自動生成.class代碼,跟我們手動寫.java代碼編譯生成的.class代碼是一樣的,自動生成有一樣好處就是一些公共的或者重復(fù)的邏輯我們可以通過自動生成來減輕我們的工作了,通常自動生成.class代碼需要用到JavaFileObject類,如下:

try {
    //packageName是包名
    JavaFileObject source = mFiler.createSourceFile(packageName);
    Writer writer = source.openWriter();
    //classStr代表的類里所有的字符
    writer.write(classStr);
    writer.flush();
    writer.close();

} catch (Exception e) {
    e.printStackTrace();
}

具體JavaFileObject的用法大家可以去搜下,因為也不復(fù)雜,這里就不多說了,因為整個類都需要我們手動寫,一是比較麻煩,二是容易出錯,square做了一個開源的javapoet庫來幫我們減少工作量,javapoet地址:https://github.com/square/javapoet

來看簡單的一個栗子:

                //創(chuàng)建method方法類
                MethodSpec methodSpec = MethodSpec.methodBuilder("getValue")
                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                        .returns(int.class)
                        .addStatement("return " + proAnnotation.value())
                        .build();
                //創(chuàng)建.class類
                TypeSpec typeSpec = TypeSpec.classBuilder("autoGenerate")
                        .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                        .addMethod(methodSpec)
                        .build();

                String packageName = processingEnv.getElementUtils().
                        getPackageOf(element).getQualifiedName().toString();
                try {
                    JavaFile javaFile = JavaFile.builder(packageName, typeSpec)
                            .addFileComment("this is auto generated")
                            .build();
                    javaFile.writeTo(filer);
                } catch (Exception e) {
                    e.printStackTrace();
                }

編譯我們的代碼,然后再build->generated->source->apt->debug目錄下就可以看到自動生成的.class類了:

// this is auto generated
package org.ninetripods.mq.javastudy;

public final class autoGenerate {
  public static int getValue() {
    return 100;
  }
}

更多用法請去javapoet的github上查看。

總結(jié)

自定義注解在一些優(yōu)秀的三方庫(如:EventBus ButterKnife等)中很常見,用于簡化我們的代碼,可以通過編譯時解析注解生成.class類,統(tǒng)一去處理,所以學(xué)習(xí)自定義注解還是很有必要的。

到此這篇關(guān)于淺析Java自定義注解的用法的文章就介紹到這了,更多相關(guān)Java自定義注解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot-application.yml多環(huán)境配置詳解

    SpringBoot-application.yml多環(huán)境配置詳解

    本文主要介紹了SpringBoot-application.yml多環(huán)境配置詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • idea新建maven項目沒有src目錄的操作方法

    idea新建maven項目沒有src目錄的操作方法

    這篇文章主要介紹了idea新建maven項目沒有src目錄的兩種操作方法,需要的朋友可以參考下
    2018-03-03
  • SpringBoot+Thymeleaf靜態(tài)資源的映射規(guī)則說明

    SpringBoot+Thymeleaf靜態(tài)資源的映射規(guī)則說明

    這篇文章主要介紹了SpringBoot+Thymeleaf靜態(tài)資源的映射規(guī)則說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • java發(fā)送url請求獲取返回值的二種方法

    java發(fā)送url請求獲取返回值的二種方法

    這篇文章主要介紹了java發(fā)送url請求獲取返回值的二種方法,需要的朋友可以參考下
    2014-03-03
  • Jmeter參數(shù)化實現(xiàn)方法及應(yīng)用實例

    Jmeter參數(shù)化實現(xiàn)方法及應(yīng)用實例

    這篇文章主要介紹了Jmeter參數(shù)化實現(xiàn)方法及應(yīng)用實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-08-08
  • Java多線程通信wait()和notify()代碼實例

    Java多線程通信wait()和notify()代碼實例

    這篇文章主要介紹了Java多線程通信wait()和notify()代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • Java啟用Azure Linux虛擬機診斷設(shè)置

    Java啟用Azure Linux虛擬機診斷設(shè)置

    這篇文章主要介紹了Java啟用Azure Linux虛擬機診斷設(shè)置,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-05-05
  • 獲取JPEGImageEncoder和JPEGCode這兩個類的方法

    獲取JPEGImageEncoder和JPEGCode這兩個類的方法

    下面小編就為大家?guī)硪黄@取JPEGImageEncoder和JPEGCode這兩個類的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • 使用controller傳boolean形式值

    使用controller傳boolean形式值

    這篇文章主要介紹了使用controller傳boolean形式值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 如何使用兩個棧實現(xiàn)隊列Java

    如何使用兩個棧實現(xiàn)隊列Java

    這篇文章主要介紹了如何使用兩個棧實現(xiàn)隊列Java,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11

最新評論