java編譯期註解處理器apt,java註解編程

本文目錄一覽:

Java 註解 apt命令如何使用

最簡單的先找到要編譯的文件所在路徑: cd 路徑 然後javac 文件名.java 然後java 文件名

java註解的類型可以是哪些

使用註解

在一般的Java開發中,最常接觸到的可能就是@Override和@SupressWarnings這兩個註解了。使用@Override的時候只需要一個簡單的聲明即可。這種稱為標記註解(marker annotation ),它的出現就代表了某種配置語義。而其它的註解是可以有自己的配置參數的。配置參數以名值對的方式出現。使用 @SupressWarnings的時候需要類似@SupressWarnings({“uncheck”, “unused”})這樣的語法。在括號裡面的是該註解可供配置的值。由於這個註解只有一個配置參數,該參數的名稱默認為value,並且可以省略。而花括號則表示是數組類型。在JPA中的@Table註解使用類似@Table(name = “Customer”, schema = “APP”)這樣的語法。從這裡可以看到名值對的用法。在使用註解時候的配置參數的值必須是編譯時刻的常量。

從某種角度來說,可以把註解看成是一個XML元素,該元素可以有不同的預定義的屬性。而屬性的值是可以在聲明該元素的時候自行指定的。在代碼中使用註解,就相當於把一部分元數據從XML文件移到了代碼本身之中,在一個地方管理和維護。

開發註解

在一般的開發中,只需要通過閱讀相關的API文檔來了解每個註解的配置參數的含義,並在代碼中正確使用即可。在有些情況下,可能會需要開發自己的註解。這在庫的開發中比較常見。註解的定義有點類似接口。下面的代碼給出了一個簡單的描述代碼分工安排的註解。通過該註解可以在源代碼中記錄每個類或接口的分工和進度情況。

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

public @interface Assignment {

    String assignee();

    int effort();

    double finished() default 0;

}

@interface用來聲明一個註解,其中的每一個方法實際上是聲明了一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型。可以通過default來聲明參數的默認值。在這裡可以看到@Retention和@Target這樣的元註解,用來聲明註解本身的行為。@Retention用來聲明註解的保留策略,有CLASS、RUNTIME和SOURCE這三種,分別表示註解保存在類文件、JVM運行時刻和源代碼中。只有當聲明為RUNTIME的時候,才能夠在運行時刻通過反射API來獲取到註解的信息。@Target用來聲明註解可以被添加在哪些類型的元素上,如類型、方法和域等。

處理註解

在程序中添加的註解,可以在編譯時刻或是運行時刻來進行處理。在編譯時刻處理的時候,是分成多趟來進行的。如果在某趟處理中產生了新的Java源文件,那麼就需要另外一趟處理來處理新生成的源文件。如此往複,直到沒有新文件被生成為止。在完成處理之後,再對Java代碼進行編譯。JDK 5中提供了apt工具用來對註解進行處理。apt是一個命令行工具,與之配套的還有一套用來描述程序語義結構的Mirror API。Mirror API(com.sun.mirror.*)描述的是程序在編譯時刻的靜態結構。通過Mirror API可以獲取到被註解的Java類型元素的信息,從而提供相應的處理邏輯。具體的處理工作交給apt工具來完成。編寫註解處理器的核心是AnnotationProcessorFactory和AnnotationProcessor兩個接口。後者表示的是註解處理器,而前者則是為某些註解類型創建註解處理器的工廠。

以上面的註解Assignment為例,當每個開發人員都在源代碼中更新進度的話,就可以通過一個註解處理器來生成一個項目整體進度的報告。 首先是註解處理器工廠的實現。

public class AssignmentApf implements AnnotationProcessorFactory {  

    public AnnotationProcessor getProcessorFor(SetAnnotationTypeDeclaration atds,? AnnotationProcessorEnvironment env) {

        if (atds.isEmpty()) {

           return AnnotationProcessors.NO_OP;

        }

        return new AssignmentAp(env); //返回註解處理器

    } 

    public CollectionString supportedAnnotationTypes() {

        return Collections.unmodifiableList(Arrays.asList(“annotation.Assignment”));

    }

    public CollectionString supportedOptions() {

        return Collections.emptySet();

    }

}

AnnotationProcessorFactory接口有三個方法:getProcessorFor是根據註解的類型來返回特定的註解處理器;supportedAnnotationTypes是返回該工廠生成的註解處理器所能支持的註解類型;supportedOptions用來表示所支持的附加選項。在運行apt命令行工具的時候,可以通過-A來傳遞額外的參數給註解處理器,如-Averbose=true。當工廠通過 supportedOptions方法聲明了所能識別的附加選項之後,註解處理器就可以在運行時刻通過AnnotationProcessorEnvironment的getOptions方法獲取到選項的實際值。註解處理器本身的基本實現如下所示。

public class AssignmentAp implements AnnotationProcessor { 

    private AnnotationProcessorEnvironment env;

    private AnnotationTypeDeclaration assignmentDeclaration;

    public AssignmentAp(AnnotationProcessorEnvironment env) {

        this.env = env;

        assignmentDeclaration = (AnnotationTypeDeclaration) env.getTypeDeclaration(“annotation.Assignment”);

    }

    public void process() {

        CollectionDeclaration declarations = env.getDeclarationsAnnotatedWith(assignmentDeclaration);

        for (Declaration declaration : declarations) {

           processAssignmentAnnotations(declaration);

        }

    }

    private void processAssignmentAnnotations(Declaration declaration) {

        CollectionAnnotationMirror annotations = declaration.getAnnotationMirrors();

        for (AnnotationMirror mirror : annotations) {

            if (mirror.getAnnotationType().getDeclaration().equals(assignmentDeclaration)) {

                MapAnnotationTypeElementDeclaration, AnnotationValue values = mirror.getElementValues();

                String assignee = (String) getAnnotationValue(values, “assignee”); //獲取註解的值

            }

        }

    }   

}

註解處理器的處理邏輯都在process方法中完成。通過一個聲明(Declaration)的getAnnotationMirrors方法就可以獲取到該聲明上所添加的註解的實際值。得到這些值之後,處理起來就不難了。

在創建好註解處理器之後,就可以通過apt命令行工具來對源代碼中的註解進行處理。 命令的運行格式是apt -classpath bin -factory annotation.apt.AssignmentApf src/annotation/work/*.java,即通過-factory來指定註解處理器工廠類的名稱。實際上,apt工具在完成處理之後,會自動調用javac來編譯處理完成後的源代碼。

JDK 5中的apt工具的不足之處在於它是Oracle提供的私有實現。在JDK 6中,通過JSR 269把自定義註解處理器這一功能進行了規範化,有了新的javax.annotation.processing這個新的API。對Mirror API也進行了更新,形成了新的javax.lang.model包。註解處理器的使用也進行了簡化,不需要再單獨運行apt這樣的命令行工具,Java編譯器本身就可以完成對註解的處理。對於同樣的功能,如果用JSR 269的做法,只需要一個類就可以了。

@SupportedSourceVersion(SourceVersion.RELEASE_6)

@SupportedAnnotationTypes(“annotation.Assignment”)

public class AssignmentProcess extends AbstractProcessor {

    private TypeElement assignmentElement; 

    public synchronized void init(ProcessingEnvironment processingEnv) {

        super.init(processingEnv);

        Elements elementUtils = processingEnv.getElementUtils();

        assignmentElement = elementUtils.getTypeElement(“annotation.Assignment”);

    } 

    public boolean process(Set? extends TypeElement annotations, RoundEnvironment roundEnv) {

        Set? extends Element elements = roundEnv.getElementsAnnotatedWith(assignmentElement);

        for (Element element : elements) {

            processAssignment(element);

        }

    }

    private void processAssignment(Element element) {

        List? extends AnnotationMirror annotations = element.getAnnotationMirrors();

        for (AnnotationMirror mirror : annotations) {

            if (mirror.getAnnotationType().asElement().equals(assignmentElement)) {

                Map? extends ExecutableElement, ? extends AnnotationValue values = mirror.getElementValues();

                String assignee = (String) getAnnotationValue(values, “assignee”); //獲取註解的值

            }

        }

    } 

}

仔細比較上面兩段代碼,可以發現它們的基本結構是類似的。不同之處在於JDK 6中通過元註解@SupportedAnnotationTypes來聲明所支持的註解類型。另外描述程序靜態結構的javax.lang.model包使用了不同的類型名稱。使用的時候也更加簡單,只需要通過javac -processor annotation.pap.AssignmentProcess Demo1.java這樣的方式即可。

上面介紹的這兩種做法都是在編譯時刻進行處理的。而有些時候則需要在運行時刻來完成對註解的處理。這個時候就需要用到Java的反射API。反射API提供了在運行時刻讀取註解信息的支持。不過前提是註解的保留策略聲明的是運行時。Java反射API的AnnotatedElement接口提供了獲取類、方法和域上的註解的實用方法。比如獲取到一個Class類對象之後,通過getAnnotation方法就可以獲取到該類上添加的指定註解類型的註解。

實例分析

下面通過一個具體的實例來分析說明在實踐中如何來使用和處理註解。假定有一個公司的僱員信息系統,從訪問控制的角度出發,對僱員的工資的更新只能由具有特定角色的用戶才能完成。考慮到訪問控制需求的普遍性,可以定義一個註解來讓開發人員方便的在代碼中聲明訪問控制權限。

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface RequiredRoles {

    String[] value();

}

下一步則是如何對註解進行處理,這裡使用的Java的反射API並結合動態代理。下面是動態代理中的InvocationHandler接口的實現。

public class AccessInvocationHandlerT implements InvocationHandler {

    final T accessObj;

    public AccessInvocationHandler(T accessObj) {

        this.accessObj = accessObj;

    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        RequiredRoles annotation = method.getAnnotation(RequiredRoles.class); //通過反射API獲取註解

        if (annotation != null) {

            String[] roles = annotation.value();

            String role = AccessControl.getCurrentRole();

            if (!Arrays.asList(roles).contains(role)) {

                throw new AccessControlException(“The user is not allowed to invoke this method.”);

            }

        }

        return method.invoke(accessObj, args);

    } 

}

在具體使用的時候,首先要通過Proxy.newProxyInstance方法創建一個EmployeeGateway的接口的代理類,使用該代理類來完成實際的操作。

java程序通常要經過五個階段。它們是什麼?

分為兩大階段

java編譯期,java運行期

編譯期,1個階段,編譯階段

運行期,4個階段,是

類加載,檢驗class文件,解析器,調用JVM

如何向Java註解添加行為

比如說在計劃使用注釋之前,比較一下當前情況下是不是注釋真的比內遷代碼和接口更為貼切。New Aspects的John Heintz在最近舉行的No Fluff Just Stuff(NFJS)俄亥俄州軟件中心研討會(COSS)上作了一個演講,討論了向Java註解添加行為的多種設計技術。定義註解與定義接口的方式相似(使用@Interface類型來聲明註解)。註解不僅可以用在包級別上,也可以用在類成員(屬性、方法、構造方法),甚至還可以用在方法的局部參數上。保持策略(retention policy)是實現註解的重要一環,根據數據在內存中保持時間的長短,有三種類型的保持策略:Runtime(一直保留數據,可以通過反射來訪問)Class(數據保留在字節碼中,無法在運行時訪問)Source(編譯器不保留該數據)一些流行框架如EJB3、Hibernate、Spring、Seam、Struts 2、RIFE及JAX-WS的新版本都開始支持註解。John論述了處理註解的三種方式:生成器:這種註解處理方式是通過讀取源代碼來產生新的源代碼,或修改現存源代碼及其它一些文件(XML、文檔等等)來實現。生成器主要依賴於容器或其它編碼約定,可以在任何保持策略下工作。使用生成器的例子有註解處理工具(Annotation Processing Tool,即APT)和處理器、XDoclet、Spoon(針對Java的擴展編譯器)、APT-Jelly(一個模板庫)等。APT不允許你修改源代碼,但對產生輔助文件卻有相當大的幫助(像WSDL、文檔)。字節碼轉換:註解處理器對帶有註解的類文件進行解析,然後對類做一些設當的修改。此外,他們也能生成其他類型的文件(比如XML配置文件)。字節碼轉換器在離線狀態(編譯期)、在裝載期都能運行,還能在運行時動態運行(使用JVMTI API)。在class或runtime這樣的保持策略下,它都能運行。使用字節碼轉換器的例子有AspectJ、Spring、Hibernate、CGLib、Javassist、ASM、BCEL等。運行時反射:這種方式使用反射API以編程的方式在運行階段檢查對象。它主要依賴於容器或其他編碼約定,同時也需要runtime保持策略。使用運行時反射的例子有:Java 5及更新Java版本中的反射、Commons Attributes。像JUnit和TestNG這樣的測試框架也使用運行時反射來處理註解。此外,John還提到了Aspect Processing的好處,如容易實現(需要好工具的支持)、語義細緻——可以影響到方法調用,甚至是屬性的訪問(在這點上,和反射和APT不同),還能夠集成多個類庫的註解,並支持特定的領域抽象。他建議開發人員說,如果Java代碼可以以接口的方式實現,那就用接口,不要去用註解。他還建議大家不要對所有東西都使用註解,因為POJO(Plain Old Java Object)要比HAJO(Heavily Annotated Java Object)好。在演講中,他列舉了其它一些最佳實踐準則,比如:註解要添加在最高層次的抽象上(比如在類和方法這兩個層次上,註解要盡量添加在類上面)在面對規則的時候,盡量使用合適的默認值,而且只對exception添加註解。比外,還要盡量少用參數。

JAVA中Annotation是什麼,有什麼用

請輸入你Annotation提供了一條與程序元素關聯任何或者任何元數據(metadata)的途徑。從某些方面看,annotation就像修飾符一樣被使用,並應用於包、類型、構造方法、方法、成員變量、參數、本地變量的聲明中。這些被存儲在annotation的“name=value”結構對中。annotation類型是一種接口,能夠通過反射API的方式提供對其的訪問。annotation能被用來為某個程序元素(類、方法、成員變量等)關聯任何的。需要注意的是,這裡存在着一個基本的潛規則:annotaion不能影響程序代碼的執行,無論增加、刪除annotation,代碼都始終如一的執行。另外,儘管一些annotation通過java的反射api方法在運行時被訪問,而java語言解釋器在工作時忽略了這些annotation。正是由於忽略了annotation,導致了annotation類型在代碼中是“不起作用”的;只有通過某種配套的工具才會對annotation類型中的進行訪問和處理。本文中將涵蓋標準的annotation和meta-annotation類型,陪伴這些annotation類型的工具是java編譯器(當然要以某種特殊的方式處理它們)。由於上述原因,annotation在使用時十分簡便。一個本地變量可以被一個以NonNull命名的annotation類型所標註,來作為對這個本地變量不能被賦予null值的斷言。而我們可以編寫與之配套的一個annotation代碼,使用它來對具有前面變量的代碼進行解析,並且嘗試驗證這個斷言。當然這些代碼並不必自己編寫。在JDK安裝後,在JDK/bin目錄中可以找到名為“apt”的工具,它提供了處理annotation的框架:它啟動後掃描源代碼中的annotation,並調用我們定義好的annotation處理器完成我們所要完成的工作(比如驗證前面例子中的斷言)。說到這裡,annotation的強大功能似乎可以替代XDoclet這類的工具了,隨着我們的深入,大家會更加堅信這一點的答案… 拿別人的,希望可以幫到你~

java 註解處理器(AbstractProcessor) 獲取到 指定註解的屬性值 javapoet 如何使用這個值生成類?

定義:註解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,注釋。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/184005.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-11-25 05:51
下一篇 2024-11-25 05:51

相關推薦

  • java client.getacsresponse 編譯報錯解決方法

    java client.getacsresponse 編譯報錯是Java編程過程中常見的錯誤,常見的原因是代碼的語法錯誤、類庫依賴問題和編譯環境的配置問題。下面將從多個方面進行分析…

    編程 2025-04-29
  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • Java騰訊雲音視頻對接

    本文旨在從多個方面詳細闡述Java騰訊雲音視頻對接,提供完整的代碼示例。 一、騰訊雲音視頻介紹 騰訊雲音視頻服務(Cloud Tencent Real-Time Communica…

    編程 2025-04-29
  • Java Bean加載過程

    Java Bean加載過程涉及到類加載器、反射機制和Java虛擬機的執行過程。在本文中,將從這三個方面詳細闡述Java Bean加載的過程。 一、類加載器 類加載器是Java虛擬機…

    編程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介紹

    本文將詳細介紹Java Milvus SearchParam withoutFields的相關知識和用法。 一、什麼是Java Milvus SearchParam without…

    編程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java語言中的一個版本,於2014年3月18日發布。本文將從多個方面對Java 8中某一周的周一進行詳細的闡述。 一、數組處理 Java 8新特性之一是Stream…

    編程 2025-04-29
  • Java判斷字符串是否存在多個

    本文將從以下幾個方面詳細闡述如何使用Java判斷一個字符串中是否存在多個指定字符: 一、字符串遍歷 字符串是Java編程中非常重要的一種數據類型。要判斷字符串中是否存在多個指定字符…

    編程 2025-04-29
  • VSCode為什麼無法運行Java

    解答:VSCode無法運行Java是因為默認情況下,VSCode並沒有集成Java運行環境,需要手動添加Java運行環境或安裝相關插件才能實現Java代碼的編寫、調試和運行。 一、…

    編程 2025-04-29
  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29
  • Java 8 Group By 會影響排序嗎?

    是的,Java 8中的Group By會對排序產生影響。本文將從多個方面探討Group By對排序的影響。 一、Group By的概述 Group By是SQL中的一種常見操作,它…

    編程 2025-04-29

發表回復

登錄後才能評論