Java注释处理器
本文是我們名為“ 高級Java ”的學院課程的一部分。
本課程旨在幫助您最有效地使用Java。 它討論了高級主題,包括對象創建,并發,序列化,反射等。 它將指導您完成Java掌握的過程! 在這里查看 !
目錄
1.簡介 2.何時使用注釋處理器 3.后臺處理注釋 4.編寫自己的注釋處理器 5.運行注釋處理器 6.接下來 7.下載源代碼1.簡介
在本部分的教程中,我們將揭開注釋處理的魔力,它通常用于檢查,修改或生成僅由注釋驅動的源代碼。 本質上,注釋處理器是Java編譯器的某種插件。 明智地使用注釋處理器可以大大簡化Java開發人員的工作,因此這就是為什么它們通常與許多流行的庫和框架捆綁在一起的原因。
作為編譯器插件還意味著注釋處理器有點底層,并且高度依賴Java版本。 然而,關于本教程的第5部分注釋知識如何以及何時使用枚舉和注釋和Java編譯器API本教程中,該部13 Java編譯器API ,將是在的內在細節的理解非常方便注釋處理器如何工作。
2.何時使用注釋處理器
正如我們簡要提到的那樣,批注處理器通常用于檢查代碼庫是否存在特定的批注,并根據用例來:
- 生成一組源文件或資源文件
- 更改(修改)現有源代碼
- 分析現有的源代碼并生成診斷消息
注釋處理器的有用性很難高估。 它們可以顯著減少開發人員必須編寫的代碼量(通過生成或修改一個),或者通過進行靜態分析來提示開發人員是否不滿足特定注釋所表示的假設。
對于開發人員而言,注解處理器幾乎是不可見的,它完全受所有現代Java IDE和流行的構建工具的支持,并且通常不需要任何特定的入侵。 在本教程的下一部分中,我們將構建自己的有些天真的注釋處理器,盡管如此,它們仍將展示此Java編譯器功能的全部功能。
3.后臺處理注釋
在深入研究我們自己的注釋處理器的實現之前,最好了解一下它的機制。 批注處理按一系列回合進行。 在每一輪中,可能會要求注釋處理器處理在上一輪產生的源文件和類文件中找到的注釋的子集。
請注意,如果要求注釋處理器在給定的回合中進行處理,則即使沒有注釋要處理,也將要求其在后續回合中進行處理,包括最后一輪。
本質上,任何Java類都可以通過實現單個接口javax.annotation.processing.Processor成為全功能注釋處理器。 但是,要真正變得可用, javax.annotation.processing.Processor每個實現都必須提供一個公共的無參數構造函數(有關更多詳細信息,請參閱本教程的第1部分“ 如何創建和銷毀對象” ),該方法可以用于實例化處理器。 處理基礎結構將遵循一組規則以與注釋處理器進行交互,并且處理器必須遵守以下協議:
- 使用處理器類的無參數構造函數創建注釋處理器的實例
- 通過適當的javax.annotation.processing.ProcessingEnvironment實例調用init方法
- 調用了getSupportedAnnotationTypes , getSupportedOptions和getSupportedSourceVersion方法(這些方法每次運行僅調用一次,而不是在每個回合中調用)
- 最后,在適當情況下,將調用javax.annotation.processing.Processor上的處理方法(請考慮到不會為每個回合創建新的注釋處理器實例)
Java文檔強調,如果在未遵循上述協議的情況下創建和使用注釋處理器實例,則該接口規范不會定義處理器的行為。
4.編寫自己的注釋處理器
我們將從最簡單的一種不變性檢查器開始,開發幾種注釋處理器。 讓我們定義一個簡單的Immutable注解,我們將使用它來對類進行注解,以確保其不允許修改其狀態。
@Target( ElementType.TYPE ) @Retention( RetentionPolicy.CLASS ) public @interface Immutable { }遵循保留策略,注釋將在編譯階段由Java編譯器保留在類文件中,但在運行時將不可用(也不應使用)。
從本教程的第3部分“ 如何設計類和接口”中我們已經知道,不可變性在Java中確實很難。 為簡單起見,我們的注釋處理器將驗證該類的所有字段都聲明為final。 幸運的是,Java標準庫提供了一個抽象注釋處理器javax.annotation.processing.AbstractProcessor ,對于大多數具體的注釋處理器而言,它被設計為方便的超類。 讓我們看一下SimpleAnnotationProcessor注釋處理器的實現。
@SupportedAnnotationTypes( "com.javacodegeeks.advanced.processor.Immutable" ) @SupportedSourceVersion( SourceVersion.RELEASE_7 ) public class SimpleAnnotationProcessor extends AbstractProcessor {@Overridepublic boolean process(final Set< ? extends TypeElement > annotations, final RoundEnvironment roundEnv) {for( final Element element: roundEnv.getElementsAnnotatedWith( Immutable.class ) ) {if( element instanceof TypeElement ) {final TypeElement typeElement = ( TypeElement )element;for( final Element eclosedElement: typeElement.getEnclosedElements() ) {if( eclosedElement instanceof VariableElement ) {final VariableElement variableElement = ( VariableElement )eclosedElement;if( !variableElement.getModifiers().contains( Modifier.FINAL ) ) {processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR,String.format( "Class '%s' is annotated as @Immutable, but field '%s' is not declared as final", typeElement.getSimpleName(), variableElement.getSimpleName() ) ); }}}}// Claiming that annotations have been processed by this processor return true;} }SupportedAnnotationTypes注釋可能是最重要的細節,它定義了此注釋處理器感興趣的注釋類型。可以在此處使用*處理所有可用的注釋。
由于提供了腳手架,因此我們的SimpleAnnotationProcessor只需要實現一個方法process 。 實現本身非常簡單,基本上只驗證要處理的類是否聲明了沒有final修飾符的任何字段。 讓我們看一下違反該天真不變性契約的類的示例。
@Immutable public class MutableClass {private String name;public MutableClass( final String name ) {this.name = name;}public String getName() {return name;} }針對此類運行SimpleAnnotationProcessor將在控制臺上輸出以下錯誤:
Class 'MutableClass' is annotated as @Immutable, but field 'name' is not declared as final因此,確認注釋處理器成功檢測到可變類上Immutable注釋的濫用。
總的來說,執行自省(和代碼生成)是大部分時間使用注釋處理器的領域。 讓我們復雜的任務一點點,從本教程中,該部13應用的Java編譯器API的一些知識的Java編譯器API 。 我們這次要編寫的注釋處理器將通過將final修飾符直接添加到類字段聲明中來更改(或修改)生成的字節碼,以確保不會在其他任何地方重新分配該字段。
@SupportedAnnotationTypes( "com.javacodegeeks.advanced.processor.Immutable" ) @SupportedSourceVersion( SourceVersion.RELEASE_7 ) public class MutatingAnnotationProcessor extends AbstractProcessor {private Trees trees; @Overridepublic void init (ProcessingEnvironment processingEnv) {super.init( processingEnv );trees = Trees.instance( processingEnv ); }@Overridepublic boolean process( final Set< ? extends TypeElement > annotations, final RoundEnvironment roundEnv) {final TreePathScanner< Object, CompilationUnitTree > scanner = new TreePathScanner< Object, CompilationUnitTree >() {@Overridepublic Trees visitClass(final ClassTree classTree, final CompilationUnitTree unitTree) {if (unitTree instanceof JCCompilationUnit) {final JCCompilationUnit compilationUnit = ( JCCompilationUnit )unitTree;// Only process on files which have been compiled from sourceif (compilationUnit.sourcefile.getKind() == JavaFileObject.Kind.SOURCE) {compilationUnit.accept(new TreeTranslator() {public void visitVarDef( final JCVariableDecl tree ) {super.visitVarDef( tree );if ( ( tree.mods.flags & Flags.FINAL ) == 0 ) {tree.mods.flags |= Flags.FINAL;}}});}}return trees;}};for( final Element element: roundEnv.getElementsAnnotatedWith( Immutable.class ) ) { final TreePath path = trees.getPath( element );scanner.scan( path, path.getCompilationUnit() );} // Claiming that annotations have been processed by this processor return true;} }實現變得更加復雜,但是許多類(例如TreePathScanner , TreePath )應該已經很熟悉了。 對同一個MutableClass類運行注釋處理器將生成以下字節碼(可以通過執行javap -p MutableClass.class命令進行驗證):
public class com.javacodegeeks.advanced.processor.examples.MutableClass {private final java.lang.String name;public com.javacodegeeks.advanced.processor.examples.MutableClass(java.lang.String);public java.lang.String getName(); }確實, name字段具有final修飾符,但在原始Java源文件中已將其省略。 我們的最后一個示例將展示注釋處理器的代碼生成功能(并結束討論)。 同樣,讓我們??實現一個注釋處理器,該處理器將通過將Immutable后綴附加到使用Immutable注釋進行注釋的類名來生成新的源文件(分別是新類)。
@SupportedAnnotationTypes( "com.javacodegeeks.advanced.processor.Immutable" ) @SupportedSourceVersion( SourceVersion.RELEASE_7 ) public class GeneratingAnnotationProcessor extends AbstractProcessor {@Overridepublic boolean process(final Set< ? extends TypeElement > annotations, final RoundEnvironment roundEnv) {for( final Element element: roundEnv.getElementsAnnotatedWith( Immutable.class ) ) {if( element instanceof TypeElement ) {final TypeElement typeElement = ( TypeElement )element;final PackageElement packageElement = ( PackageElement )typeElement.getEnclosingElement();try {final String className = typeElement.getSimpleName() + "Immutable";final JavaFileObject fileObject = processingEnv.getFiler().createSourceFile(packageElement.getQualifiedName() + "." + className);try( Writer writter = fileObject.openWriter() ) {writter.append( "package " + packageElement.getQualifiedName() + ";" );writter.append( "\\n\\n");writter.append( "public class " + className + " {" );writter.append( "\\n");writter.append( "}");}} catch( final IOException ex ) {processingEnv.getMessager().printMessage(Kind.ERROR, ex.getMessage());}}}// Claiming that annotations have been processed by this processor return true;} }將這個注釋處理器注入MutableClass類的編譯過程的結果是,將生成以下文件:
package com.javacodegeeks.advanced.processor.examples;public class MutableClassImmutable { }盡管如此,源文件及其類是使用原始字符串連接生成的(事實上,該類實際上是非常無用的),目的是演示注釋處理器執行的代碼生成是如何工作的,因此可以應用更復雜的生成技術。
5.運行注釋處理器
Java編譯器通過支持–processor命令行參數,可以輕松地將任意數量的注釋處理器插入到編譯過程中。 例如,這是通過在MutableClass.java源文件的編譯期間將其作為javac工具的參數傳遞而運行MutatingAnnotationProcessor的一種方法:
javac -cp processors/target/advanced-java-part-14-java7.processors-0.0.1-SNAPSHOT.jar -processor com.javacodegeeks.advanced.processor.MutatingAnnotationProcessor -d examples/target/classesexamples/src/main/java/com/javacodegeeks/advanced/processor/examples/MutableClass.java僅編譯一個文件看起來并不復雜,但是現實生活中的項目包含成千上萬的Java源文件,而從命令行使用javac工具來編譯這些文件實在是太過分了。 社區很可能已經開發了很多很棒的構建工具(例如Apache Maven , Gradle , sbt , Apache Ant等等),這些工具負責調用Java編譯器并做很多其他事情,因此,如今大多數Java項目在那里至少使用其中之一。 例如,以下是從Apache Maven構建文件( pom.xml )調用MutatingAnnotationProcessor的方法:
<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><source>1.7</source><target>1.7</target><annotationProcessors> <proc>com.javacodegeeks.advanced.processor.MutatingAnnotationProcessor</proc></annotationProcessors></configuration> </plugin>6.接下來
在本教程的這一部分中,我們對注解處理器及其幫助檢查源代碼,變異(修改)結果字節碼或生成新的Java源文件或資源的方式進行了深入研究。 批注處理器通常用于使Java開發人員從遍布整個代碼庫的批注中衍生出來,從而免于編寫大量樣板代碼。 在本教程的下一部分中,我們將研究Java代理以及操作JVM在運行時解釋字節碼的方式。
7.下載源代碼
您可以在此處下載本課程的源代碼: advanced-java-part-14
翻譯自: https://www.javacodegeeks.com/2015/09/java-annotation-processors.html
總結
- 上一篇: 亚马逊标题自动抓取_15分钟内开始使用A
- 下一篇: 腾讯邮箱下载电脑版(腾讯邮箱电脑版客户端