日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

标记接口,注解和注解处理器的前世今生

發布時間:2024/2/28 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 标记接口,注解和注解处理器的前世今生 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 簡介
  • 注解的起源和marker interfaces
  • 注解的定義
    • Retention
    • Target
    • 自定義參數
  • 在運行時使用注解
  • 在編譯時使用注解
  • 總結

簡介

相信大部分的開發者都用過注解,尤其是對使用過Spring的開發者來說,注解是現代Spring中不可獲取的一部分。Spring從最開始的xml配置到后面的注解配置,不論是從編程習慣還是項目的構建,都對我們程序員產生了非常重要的影響。

除了使用Spring自帶的注解之外,我們還可以自定義注解。然后通過AOP來對注解進行攔截從而處理相應的業務邏輯。

除了Spring之外,其實JDK本身自帶注解,本文將會深入探討注解的起源和兩種不同的使用方式。

更多精彩內容且看:

  • 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
  • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
  • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
  • java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

更多內容請訪問www.flydean.com

注解的起源和marker interfaces

先看一個最簡單的注解:

@CustUserAnnotation public class CustUser { }

上面我們將CustUser標記為一個自定義的注解@CustUserAnnotation。

注解其實是在JDK 5中引入的。那么在JDK 5之前,注解是用什么方式來表示的呢?答案就是marker interfaces。

marker interfaces中文翻譯叫做標記接口,標記接口就是說這個接口使用來做標記用的,內部并沒有提供任何方法或者字段。

在java中有很多標記接口,最常見的就是Cloneable,Serializable,還有java.util包中的EventListener和RandomAccess。

以Cloneable為例:

/** @since 1.0*/ public interface Cloneable { }

該接口從java1.0就開始有了。實現該接口的類才能夠調用Object中的clone方法。

我們在代碼中如何判斷類是否實現了Cloneable接口呢?

public Object clone() throws CloneNotSupportedException {if (this instanceof Cloneable) {return super.clone();} else {throw new CloneNotSupportedException();}}

很簡單,通過instanceof來判斷是否是Cloneable即可。

marker interfaces好用是好用,但是有一些缺點,比如沒有額外的元數據信息,功能太過單一,并且會和正常的interface混淆。實現起來也比一般的interface復雜。

正式由于這些原因,在JDK5中,引入了注解Annotation。

注解的定義

注解是由@interface來定義的。創建一個annotation需要指定其target和retention,并可以自定義參數。

我們舉個例子:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface CustUserAnnotation {int value();String name();String[] addresses(); }

上面是我自定義的一個注解。

Retention

Retention表示注解將會在什么階段可見。它有三個可選值:

SOURCE 表示只在源代碼可見,編譯的時候就會被丟棄。

CLASS 表示在class可見,也就是說編譯的時候可見,但是運行時候不可見。

RUNTIME 表示運行時候可見。什么時候才需要運行時可見呢?那就是使用到反射的時候。我們會在后面的例子中具體的描述這種情況。

Retention本身也是一個注解:

@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention {/*** Returns the retention policy.* @return the retention policy*/RetentionPolicy value(); }

Target

Target表示這個注解將會用到什么地方。它有12個值。

TYPE 表示用在Class,interface,enum或者record上。

FIELD 表示用在class的字段上。

METHOD 表示用在方法上。

PARAMETER 表示用在方法上面。

CONSTRUCTOR 用在構造函數上。

LOCAL_VARIABLE 用在本地變量上。

ANNOTATION_TYPE 用在注解上。

PACKAGE 用在package上。

TYPE_PARAMETER 用在類型參數上。

TYPE_USE 用在任何TYPE使用上。

TYPE_PARAMETER和TYPE_USE有什么區別呢?

TYPE_USE用在任何類型的使用上面,比如申明,泛型,轉換:

@Encrypted String data List<@NonNull String> strings MyGraph = (@Immutable Graph) tmpGraph;

而TYPE_PARAMETER用在類型參數上:

class MyClass<T> {...}

MODULE 用在module上。

RECORD_COMPONENT 預覽功能,和records相關。

Target和Retention一樣也是一個注解。

@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target {/*** Returns an array of the kinds of elements an annotation type* can be applied to.* @return an array of the kinds of elements an annotation type* can be applied to*/ElementType[] value(); }

自定義參數

注解也可以自定參數,參數可以是下的類型:

  • 基礎類型:int,long,double等
  • String
  • Class
  • 枚舉類型
  • 其他的注解類型
  • 上面5中的數組
  • 上面我們的自定義類型定義了三個參數:

    int value();String name();String[] addresses();

    我們看下怎么使用:

    @CustUserAnnotation(value = 100, name="jack ma",addresses = {"人民路","江西路"}) public class CustUser { }

    在使用中,我們需要傳入自定義的參數,當然你也可以使用default在注解中提供默認值,這樣就不需要從外部傳入。

    在運行時使用注解

    在運行時,我們可以使用反射的API來獲得注解,并獲取注解中的自定義變量,從而進行相應的業務邏輯處理。

    CustUser custUser= new CustUser();Annotation[] annotations= custUser.getClass().getAnnotations();Stream.of(annotations).filter(annotation -> annotation instanceof CustUserAnnotation).forEach(annotation -> log.info(((CustUserAnnotation) annotation).name()));

    還是剛才的例子,我們通過getAnnotations方法獲取到注解的值。

    在運行時是用注解當然是個不錯的主意,但是反射用的太多的話其實會影響程序的性能。

    那么我們可以不可以將運行時的注解提前到編譯時呢?答案是肯定的。

    在編譯時使用注解

    要想在編譯時使用注解,就要介紹今天我們的最后一部分內容annotation processors。

    自定義processors需要實現javax.annotation.processing.Processor接口。

    接下來我們自定義一個Processor:

    @SupportedAnnotationTypes("com.flydean.*") @SupportedSourceVersion(SourceVersion.RELEASE_14) public class MyProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {System.out.println("process annotation!");annotations.forEach(annotation -> {Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotation);elements.stream().filter(TypeElement.class::isInstance).map(TypeElement.class::cast).map(TypeElement::getQualifiedName).map(name -> "Class " + name + " is annotated with " + annotation.getQualifiedName()).forEach(System.out::println);});return true;} }

    SupportedAnnotationTypes表示支持的注解類型。

    SupportedSourceVersion表示支持的源代碼版本。

    最后我們在process方法中,獲取了注解類的一些信息。

    有了processor我們怎么在maven環境中使用呢?

    最簡單的辦法就是在maven的maven-compiler-plugin插件中添加annotationProcessors,如下所示:

    <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>14</source><target>14</target><annotationProcessors><annotationProcessor>com.flydean.MyProcessor</annotationProcessor></annotationProcessors></configuration></plugin></plugins></build>

    如果不添加,默認情況下編譯器會從classpath中去尋找META-INF/services/javax.annotation.processing.Processor文件,這個文件里面列出了對外提供的注解處理器。編譯器會加載這些注解處理器去處理當前項目的注解。

    lombok應該大家都用過吧,它實際上為我們提供了兩個注解處理器:

    很不幸的是,因為我在CustUser中使用了lombok中的log,如果像上面一樣顯示指定annotationProcessor則會將覆蓋默認的查找路徑,最后會導致lombok失效。

    那應該怎么處理才能兼容lombok和自定義的processor呢?

    我們可以把自定義processor單獨成一個模塊,也做成lombok這樣的形式:

    這個processor的模塊編譯參數需要加上一個proc none的參數:

    <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>14</source><target>14</target><proc>none</proc></configuration></plugin></plugins></build>

    proc是設置是否需要在本項目中啟用processor。對于processor項目來說,它本身還沒有編譯,如果啟用就會出現找不到類的錯誤。所以這里我們需要將proc設置為none。

    最后我們的annotation-usage項目可以不需要annotationProcessors的配置就可以自動從classpath中讀取到自定義的processor了。

    總結

    本文介紹了marker interface,annotation和annotation processor,并詳細講解了如何在maven程序中使用他們。

    本文的例子https://github.com/ddean2009/
    learn-java-base-9-to-20

    本文作者:flydean程序那些事

    本文鏈接:http://www.flydean.com/marker-interface-annotation-processor/

    本文來源:flydean的博客

    歡迎關注我的公眾號:程序那些事,更多精彩等著您!

    總結

    以上是生活随笔為你收集整理的标记接口,注解和注解处理器的前世今生的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。