国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

簡單介紹 Java 中的編譯時注解

solocoder / 1833人閱讀

摘要:如果在中沒有找到該錯誤請通過報告頁建立該編譯器。請在報告中附上您的程序和以下診斷信息。

1. 前言

上一篇 主要介紹了什么是 注解 (Annotation) 以及如何讀取 運行時注解 中的數據, 同時用注解實現了簡單的 ORM 功能. 這次介紹另一部分: 如何讀取 編譯時注解 ( RetentionPolicy.SOURCE )

2. 作用

編譯時注解可以用來動態生成代碼. 使用 SOURCE 類型注解的代碼會在編譯時被解析, 生成新的 java 文件, 然后和原來的 java 文件一起編譯成字節碼. 由于不使用反射功能, 編譯時注解不會拖累性能, 因而被許多框架使用, 比如 Butter Knife, Dragger2 等.

3. 例子 1. 代碼

還是從簡單的例子開始看. 這里要做的是生成一個 java 類, 其擁有一個打印注解信息的方法.
先定義一個注解

package apt;
......
@Retention(RetentionPolicy.SOURCE) // 注解只在源碼中保留
@Target(ElementType.TYPE) // 用于修飾類
public @interface Hello {
    String name() default "";
}

使用注解的類

package apt;
@Hello(name = "world")
public class Player {
}

不使用注解的類, 用于對比

package apt;
public class Ignored {
}

上一篇說過, 注解沒有行為, 只有數據, 需要對應的處理器才能發揮作用. javac 提供了解析編譯時注解的注解處理器 ( Annotation Processor ). 對于自定義的注解, 需要手動實現它的注解處理器.下面來看一個簡單的注解處理器實現.

package apt;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.Writer;
import java.util.Set;

/**
 * Created by away on 2017/6/12.
 */
@SupportedSourceVersion(SourceVersion.RELEASE_8) // 源碼級別, 這里的環境是 jdk 1.8
@SupportedAnnotationTypes("apt.Hello") // 處理的注解類型, 這里需要處理的是 apt 包下的 Hello 注解(這里也可以不用注解, 改成重寫父類中對應的兩個方法)
public class HelloProcessor extends AbstractProcessor {

    // 計數器, 用于計算 process() 方法運行了幾次
    private int count = 1;

    // 用于寫文件
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
    }

    // 處理編譯時注解的方法
    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnv) {
        System.out.println("start process, count = " + count++);
        // 獲得所有類
        Set rootElements = roundEnv.getRootElements();
        System.out.println("all class:");

        for (Element rootElement : rootElements) {
            System.out.println("  " + rootElement.getSimpleName());
        }

        // 獲得有注解的元素, 這里 Hello 只能修飾類, 所以只有類
        Set elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(Hello.class);
        System.out.println("annotated class:");
        for (Element element : elementsAnnotatedWith) {
            String className = element.getSimpleName().toString();
            System.out.println("  " + className);

            String output = element.getAnnotation(Hello.class).name();
            // 產生的動態類的名字
            String newClassName = className + "_New";
            // 寫 java 文件
            createFile(newClassName, output);
        }
        return true;
    }

    private void createFile(String className, String output) {
        StringBuilder cls = new StringBuilder();
        cls.append("package apt;

public class ")
                .append(className)
                .append(" {
  public static void main(String[] args) {
")
                .append("    System.out.println("")
                .append(output)
                .append("");
  }
}");
        try {
            JavaFileObject sourceFile = filer.createSourceFile("apt." + className);
            Writer writer = sourceFile.openWriter();
            writer.write(cls.toString());
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代碼的邏輯很簡單:

獲得所有標有注解的類

取出注解中的信息

生成新的 java 文件

這里只需要知道, 自定義注解處理器要繼承 AbstractProcessor 類, 并重寫 process 方法.

2. 運行

此時項目目錄如下, 這里 out 目錄為手動創建

out

production

apt

src

apt

在命令行中進入項目根目錄, 即 src 文件夾的上一層.

首先編譯注解處理器: javac -encoding UTF-8 -d outproduction srcaptHelloProcessor.java srcaptHello.java

接著執行注解處理器: javac -encoding UTF-8 -cp outproduction -processor apt.HelloProcessor -d outproduction -s src srcapt*.java

得到如下輸出

start process, count = 1
all class:
  Hello
  HelloProcessor
  Ignored
  Player
annotated class:
  Player
start process, count = 2
all class:
  Player_New
annotated class:
start process, count = 3
all class:
annotated class:

這時 src/apt 目錄下會出現新的 Player_New.java 文件, 內容如下

package apt;

public class Player_New {
  public static void main(String[] args) {
    System.out.println("world");
  }
}

執行 java -cp outproductionelevator apt.Player_New
得到輸出 world.

到這里, 編譯時注解便處理成功了. 我們定義了一個極其簡單的注解處理器, 讀取了注解信息, 并生成了新的 java 類來打印該信息.

這里可能會報一個錯誤

編譯器 (1.8.0_131) 中出現異常錯誤。如果在 Bug Database (http://bugs.java.com) 中沒有找到該錯誤, 請通過 Java Bug 報告頁 (http://bugreport.java.com) 建立該 Java 編譯器 Bug。請在報告中附上您的程序和以下診斷信息。謝謝。
java.lang.IllegalStateException: endPosTable already set
...
...

這時把產生的 Player_New.java 文件刪去重新執行注解處理器就好了

3. javac

這里稍微解釋一下 javac 命令, IDE 用多了, 寫的時候都忘得差不多了 (:зゝ∠)
javac 用于啟動 java 編譯器, 格式為 javac , 其中 的格式為 -xx xxxx, 都是配對出現的, 用于指定一些信息.

這里 的位置并沒有講究, 只要在 javac 后面就行了, 在兩個 xxx.java 之間出現也是可以的, 比如: javac -d outproduction srcaptHelloProcessor.java -encoding UTF-8 srcaptHello.java 正常執行.

一些

-cp <路徑>

-classpath <路徑> 一樣, 用于指定查找用戶類文件和注釋處理程序的位置

-d <目錄>

指定放置生成的類文件的位置

-s <目錄>

指定放置生成的源文件的位置

-processorpath <路徑>

指定查找注釋處理程序的位置

不寫的話會使用 -cp 的位置

-processor [,,...]

要運行的注釋處理程序的名稱; 繞過默認的搜索進程

4. 問題

到這里應該會有一些問題, 比如

AbstractProcessor, Elememt 分別是什么

process 為什么執行了 3 次

運行注解處理器的時候會啟動 jvm

這里先說一下第三個問題. javac 運行注解處理器的時候, 會開一個完整的 java 虛擬機執行代碼, 所以自定義的注解處理器是可以使用各種類庫的.
接下來講一下一些基本概念, 用來回答上面兩個問題.

4.概念 1. AbstractProcessor

這是處理器的API,所有的處理器都是基于 AbstractProcessor, 它實現了接口 Processor

接口

void init(ProcessingEnvironment processingEnv);

會被注解處理工具調用, ProcessingEnvironment 提供了一些實用的工具類 Elements, TypesFiler.

boolean process(Set annotations, RoundEnvironment roundEnv);

相當于 main 函數, 是注解處理器的入口. 輸入參數 RoundEnviroment 可以查詢出包含特定注解的被注解元素

SourceVersion getSupportedSourceVersion();

用來指定使用的 java 版本

Set getSupportedAnnotationTypes();

指定這個注解處理器是注冊給哪個注解的, 這里需要用注解的全稱, 比如上面的 apt.Hello

最后兩個也可以用注解的形式實現, 例子中的代碼就是這么做的

2. Element

程序的元素, 例如包, 類或者方法. 每個 Element 代表一個靜態的, 語言級別的構件. 可以參考下面的代碼理解

package com.example;    // PackageElement

public class Foo {        // TypeElement
    private int a;      // VariableElement
    private Foo other;  // VariableElement
    public Foo () {}    // ExecuteableElement
    public void setA (  // ExecuteableElement
                     int newA   // TypeElement
                     ) {}
}

由此可見 roundEnv.getElementsAnnotatedWith(xxx.class) 得到的并不一定是類, 也可能是方法, 成員變量等, 只是例子中用的注解只能用于修飾類.

3. 注解處理器的執行

javadoc 中對此的描述如下

Annotation processing happens in a sequence of rounds. On each round, a processor may be asked to process a subset of the annotations found on the source and class files produced by a prior round. The inputs to the first round of processing are the initial inputs to a run of the tool; these initial inputs can be regarded as the output of a virtual zeroth round of processing.

概況來說, 就是 process() 方法會被調用多次, 直到沒有新的類產生為止.
因為新生成的文件中也可能包含 @Hello 注解,它們會繼續被 HelloProcessor 處理.

Round input output
1 Hello.java
HelloProcessor.java
Ignored.java
Player.java
Player_New.java
2 Player_New.java -
3 - -

下一篇會開始分析 Butter Knife 的源碼.

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70117.html

相關文章

  • 簡單介紹 Java 中的注解 (Annotation)

    摘要:例子首先來看一個例子這里用了目的是告訴編譯器這個方法重寫了父類的方法如果編譯器發現父類中沒有這個方法就會報錯這個注解的作用大抵是防止手滑寫錯方法同時增強了程序的可讀性這里需要指出一點去掉并不會影響程序的執行只是起到標記的作用找到的實現關注點 1. 例子 首先來看一個例子: @Override public String toString() { return xxxxx; ...

    LMou 評論0 收藏0
  • Java? 教程(注解

    注解 注解(一種元數據形式)提供有關不屬于程序本身的程序的數據,注解對它們注解的代碼的操作沒有直接影響。 注解有許多用途,其中包括: 編譯器的信息 — 編譯器可以使用注解來檢測錯誤或抑制警告。 編譯時和部署時處理 — 軟件工具可以處理注解信息以生成代碼、XML文件等。 運行時處理 — 可以在運行時檢查某些注解。 本課程介紹了可以使用注解的位置,以及如何應用注解,Java平臺標準版(Java S...

    econi 評論0 收藏0
  • Java 注解實戰

    摘要:注解是的一個新特性。很重要,生產中我們開發常用此值表示注解是否可被子元素繼承。類注解方法注解通過反射獲取方法對象此部分內容可參考通過反射獲取注解信息注解處理器實戰接下來我通過在公司中的一個實戰改編來演示一下注解處理器的真實使用場景。 前言:Java 注解,對于很多人都不陌生了,但是在公司的實際開發中,可能讓我們自己去定義注解并應用到生產環境中的機會比較少,所以會導致一部分人對注解的理解...

    Jochen 評論0 收藏0
  • APT案例之點擊事件

    摘要:楊充一定時間內該點擊事件只能執行一次用來修飾這是一個什么類型的注解。楊充自定義編譯器獲取遍歷,并生成代碼配置文件文件配置的作用是向系統注冊自定義注解處理器,執行編譯時使用進行處理。 目錄介紹 01.創建項目步驟 1.1 項目搭建 1.2 項目功能 02.自定義注解 03.創建Processor 04.compiler配置文件 05.編譯jar 06.如何使用 07.編譯生成代...

    cyixlq 評論0 收藏0
  • 關于Apt注解實踐與總結【包含20篇博客】

    摘要:使用實現功能運行期注解案例使用簡單的注解,便可以設置布局,等效于使用實現路由綜合型案例比較全面的介紹從零起步,一步一步封裝簡易的路由開源庫。申明注解用的就是。返回值表示這個注解里可以存放什么類型值。 YCApt關于apt方案實踐與總結 目錄介紹 00.注解系列博客匯總 01.什么是apt 02.annotationProcessor和apt區別 03.項目目錄結構 04.該案例作用 ...

    gnehc 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<