亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

Java注解處理器學習之編譯時處理的注解詳析

2024-07-14 08:40:46
字體:
來源:轉載
供稿:網友

1. 一些基本概念

在開始之前,我們需要聲明一件重要的事情是:我們不是在討論在運行時通過反射機制運行處理的注解,而是在討論在編譯時處理的注解。

編譯時注解跟運行時注解到底區別在什么地方?其實說大也不大,主要是考慮到性能上面的問題。運行時注解主要是完全依賴于反射,反射的效率比原生的慢,所以在內存比較少,CPU比較爛的機器上會有一些卡頓現象出現。而編譯時注解完全不會有這個問題,因為它在我們編譯過程(javascript/57140.html">java->class)中,通過一些注解標示,去動態生成一些類或者文件,所以跟我們的APK運行完全沒有任何關系,自然就不存在性能上的問題。所以一般比較著名的開源項目如果采用注解功能,通常采用編譯時注解

注解處理器是 javac 自帶的一個工具,用來在編譯時期掃描處理注解信息。你可以為某些注解注冊自己的注解處理器。這里,我假設你已經了解什么是注解及如何自定義注解。如果你還未了解注解的話,可以查看官方文檔。注解處理器在 Java 5 的時候就已經存在了,但直到 Java 6 (發布于2006看十二月)的時候才有可用的API。過了一段時間java的使用者們才意識到注解處理器的強大。所以最近幾年它才開始流行。

一個特定注解的處理器以 java 源代碼(或者已編譯的字節碼)作為輸入,然后生成一些文件(通常是.java文件)作為輸出。那意味著什么呢?你可以生成 java 代碼!這些 java 代碼在生成的.java文件中。因此你不能改變已經存在的java類,例如添加一個方法。這些生成的 java 文件跟其他手動編寫的 java 源代碼一樣,將會被 javac 編譯。

Annotation processing是在編譯階段執行的,它的原理就是讀入Java源代碼,解析注解,然后生成新的Java代碼。新生成的Java代碼最后被編譯成Java字節碼,注解解析器(Annotation Processor)不能改變讀入的Java 類,比如不能加入或刪除Java方法。

2. AbstractProcessor

讓我們來看一下處理器的 API。所有的處理器都繼承了AbstractProcessor,如下所示:

package com.example;import java.util.LinkedHashSet;import java.util.Set;import javax.annotation.processing.AbstractProcessor;import javax.annotation.processing.ProcessingEnvironment;import javax.annotation.processing.RoundEnvironment;import javax.annotation.processing.SupportedAnnotationTypes;import javax.annotation.processing.SupportedSourceVersion;import javax.lang.model.SourceVersion;import javax.lang.model.element.TypeElement;public class MyProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annoations,  RoundEnvironment env) { return false; } @Override public Set<String> getSupportedAnnotationTypes() { Set<String> annotataions = new LinkedHashSet<String>(); annotataions.add("com.example.MyAnnotation"); return annotataions; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); }}

init(ProcessingEnvironment processingEnv) :所有的注解處理器類都必須有一個無參構造函數。然而,有一個特殊的方法init(),它會被注解處理工具調用,以ProcessingEnvironment作為參數。ProcessingEnvironment 提供了一些實用的工具類Elements, Types和Filer。我們在后面將會使用到它們。

process(Set<? extends TypeElement> annoations, RoundEnvironment env)  :這類似于每個處理器的main()方法。你可以在這個方法里面編碼實現掃描,處理注解,生成 java 文件。使用RoundEnvironment 參數,你可以查詢被特定注解標注的元素(原文:you can query for elements annotated with a certain annotation )。后面我們將會看到詳細內容。

getSupportedAnnotationTypes():在這個方法里面你必須指定哪些注解應該被注解處理器注冊。注意,它的返回值是一個String集合,包含了你的注解處理器想要處理的注解類型的全稱。換句話說,你在這里定義你的注解處理器要處理哪些注解。

getSupportedSourceVersion() : 用來指定你使用的 java 版本。通常你應該返回SourceVersion.latestSupported() 。不過,如果你有足夠的理由堅持用 java 6 的話,你也可以返回SourceVersion.RELEASE_6。我建議使用SourceVersion.latestSupported() 。在 Java 7 中,你也可以使用注解的方式來替代重寫getSupportedAnnotationTypes() getSupportedSourceVersion() ,如下所示:

@SupportedSourceVersion(value=SourceVersion.RELEASE_7)@SupportedAnnotationTypes({ // Set of full qullified annotation type names "com.example.MyAnnotation", "com.example.AnotherAnnotation" })public class MyProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annoations,  RoundEnvironment env) { return false; } @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); }}

由于兼容性問題,特別是對于 android ,我建議重寫getSupportedAnnotationTypes() getSupportedSourceVersion() ,而不是使用 @SupportedAnnotationTypes @SupportedSourceVersion。

接下來你必須知道的事情是:注解處理器運行在它自己的 JVM 中。是的,你沒看錯。javac 啟動了一個完整的 java 虛擬機來運行注解處理器。這意味著什么?你可以使用任何你在普通 java 程序中使用的東西。使用 guava! 你可以使用依賴注入工具,比如dagger或者任何其他你想使用的類庫。但不要忘記,即使只是一個小小的處理器,你也應該注意使用高效的算法及設計模式,就像你在開發其他 java 程序中所做的一樣。

3. 注冊你的處理器

你可能會問 “怎樣注冊我的注解處理器到 javac ?”。你必須提供一個.jar文件。就像其他 .jar 文件一樣,你將你已經編譯好的注解處理器打包到此文件中。并且,在你的 .jar 文件中,你必須打包一個特殊的文件javax.annotation.processing.Processor到META-INF/services目錄下。因此你的 .jar 文件目錄結構看起來就你這樣:

MyProcess.jar -com  -example   -MyProcess.class -META-INF  -services   -javax.annotation.processing.Processor

javax.annotation.processing.Processor 文件的內容是一個列表,每一行是一個注解處理器的全稱。例如:

com.example.MyProcess

com.example.AnotherProcess

4. 例子:工廠模式

我們要解決的問題是:我們要實現一個 pizza 店,這個 pizza 店提供給顧客兩種 pizza (Margherita 和 Calzone),還有甜點 Tiramisu(提拉米蘇)。

public interface Meal { public float getPrice();}public class MargheritaPizza implements Meal{ @Override public float getPrice() {  return 6.0f; }}public class CalzonePizza implements Meal{ @Override public float getPrice() {  return 8.5f; }}public class Tiramisu implements Meal{ @Override public float getPrice() {  return 4.5f; }}public class PizzaStore { public Meal order(String mealName) {  if (null == mealName) {   throw new IllegalArgumentException("name of meal is null!");  }  if ("Margherita".equals(mealName)) {   return new MargheritaPizza();  }  if ("Calzone".equals(mealName)) {   return new CalzonePizza();  }  if ("Tiramisu".equals(mealName)) {   return new Tiramisu();  }  throw new IllegalArgumentException("Unknown meal '" + mealName + "'"); } private static String readConsole() {  Scanner scanner = new Scanner(System.in);  String meal = scanner.nextLine();  scanner.close();  return meal; }  public static void main(String[] args) {  System.out.println("welcome to pizza store");  PizzaStore pizzaStore = new PizzaStore();  Meal meal = pizzaStore.order(readConsole());  System.out.println("Bill:$" + meal.getPrice()); }}

正如你所見,在order()方法中,我們有許多 if 條件判斷語句。并且,如果我們添加一種新的 pizza 的話,我們就得添加一個新的 if 條件判斷。但是等一下,使用注解處理器和工廠模式,我們可以讓一個注解處理器生成這些 if 語句。如此一來,我們想要的代碼就像這樣子:

public class PizzaStore { private MealFactory factory = new MealFactory();  public Meal order(String mealName) {  return factory.create(mealName); } private static String readConsole() {  Scanner scanner = new Scanner(System.in);  String meal = scanner.nextLine();  scanner.close();  return meal; }  public static void main(String[] args) {  System.out.println("welcome to pizza store");  PizzaStore pizzaStore = new PizzaStore();  Meal meal = pizzaStore.order(readConsole());  System.out.println("Bill:$" + meal.getPrice()); }}public class MealFactory { public Meal create(String id) {  if (id == null) {   throw new IllegalArgumentException("id is null!");  }  if ("Calzone".equals(id)) {   return new CalzonePizza();  }  if ("Tiramisu".equals(id)) {   return new Tiramisu();  }  if ("Margherita".equals(id)) {   return new MargheritaPizza();  }  throw new IllegalArgumentException("Unknown id = " + id); }}

5. @Factory Annotation

能猜到么,我們打算使用注解處理器生成MealFactory類。更一般的說,我們想要提供一個注解和一個處理器用來生成工廠類。

讓我們看一下@Factory注解:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.CLASS)public @interface Factory { /**  * The name of the factory  */ Class<?> type(); /**  * The identifier for determining which item should be instantiated  */ String id();}

思想是這樣的:我們注解那些食物類,使用type()表示這個類屬于哪個工廠,使用id()表示這個類的具體類型。讓我們將@Factory注解應用到這些類上吧:

@Factory(type=MargheritaPizza.class, id="Margherita")public class MargheritaPizza implements Meal{ @Override public float getPrice() {  return 6.0f; }}@Factory(type=CalzonePizza.class, id="Calzone")public class CalzonePizza implements Meal{ @Override public float getPrice() {  return 8.5f; }}@Factory(type=Tiramisu.class, id="Tiramisu")public class Tiramisu implements Meal{ @Override public float getPrice() {  return 4.5f; }}

你可能會問,我們是不是可以只將@Factory注解應用到Meal接口上?答案是不行,因為注解是不能被繼承的。即在class X上有注解,class Y extends X,那么class Y是不會繼承class X上的注解的。在我們編寫處理器之前,需要明確幾點規則:

  • 只有類能夠被@Factory注解,因為接口和虛類是不能通過new操作符實例化的。
  • 被@Factory注解的類必須提供一個默認的無參構造函數。否則,我們不能實例化一個對象。
  • 被@Factory注解的類必須直接繼承或者間接繼承type指定的類型。(或者實現它,如果type指定的是一個接口)
  • 被@Factory注解的類中,具有相同的type類型的話,這些類就會被組織起來生成一個工廠類。工廠類以Factory作為后綴,例如:type=Meal.class將會生成MealFactory類。
  • id的值只能是字符串,且在它的type組中必須是唯一的。

注解處理器:

public class FactoryProcessor extends AbstractProcessor { private Types typeUtils; private Elements elementUtils; private Filer filer; private Messager messager; private Map<String, FactoryGroupedClasses> factoryClasses =    new LinkedHashMap<String, FactoryGroupedClasses>(); @Override public synchronized void init(ProcessingEnvironment processingEnv) {  super.init(processingEnv);  typeUtils = processingEnv.getTypeUtils();  elementUtils = processingEnv.getElementUtils();  filer = processingEnv.getFiler();  messager = processingEnv.getMessager(); } @Override public boolean process(Set<? extends TypeElement> arg0,   RoundEnvironment arg1) {  ...  return false; } @Override public Set<String> getSupportedAnnotationTypes() {  Set<String> annotataions = new LinkedHashSet<String>();  annotataions.add(Factory.class.getCanonicalName());  return annotataions; } @Override public SourceVersion getSupportedSourceVersion() {  return SourceVersion.latestSupported(); }}

getSupportedAnnotationTypes()方法中,我們指定@Factory注解將被這個處理器處理。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品69久久| 亚洲自拍在线观看| 亚洲综合中文字幕在线观看| 亚洲成人av在线播放| 亚洲一区二区三区视频播放| 精品久久久久久中文字幕大豆网| 2021国产精品视频| 91高清视频免费| 精品高清一区二区三区| 亚洲精品国产精品久久清纯直播| 国产成人一区二区三区电影| 97免费中文视频在线观看| 亚洲天堂男人的天堂| 国产精品视频精品| 亚洲欧美国产日韩中文字幕| 久热精品视频在线观看一区| 欧美激情女人20p| 欧美精品免费播放| 夜色77av精品影院| 影音先锋欧美在线资源| 成人精品视频久久久久| 久久伊人精品一区二区三区| 成人福利在线视频| 992tv在线成人免费观看| 国产精品激情自拍| 国产在线a不卡| 亚洲欧美国产精品专区久久| 久久天堂av综合合色| 欧美久久精品一级黑人c片| 欧美日韩视频在线| 92版电视剧仙鹤神针在线观看| 欧美日韩成人黄色| 国产精品av电影| 国产精品一区二区久久久久| 国产欧美日韩精品在线观看| 日本高清不卡在线| 色哟哟入口国产精品| 亚洲欧美色图片| 国产精品国产三级国产专播精品人| 亚洲国产精品99| 亚洲性日韩精品一区二区| 中文字幕视频一区二区在线有码| 久久久久国产视频| 在线视频国产日韩| 一区二区国产精品视频| 欧美激情按摩在线| 国产+人+亚洲| 中文一区二区视频| 伊人久久久久久久久久久| 欧美激情一二三| 国模视频一区二区三区| 国产91久久婷婷一区二区| 最新国产精品拍自在线播放| www.国产精品一二区| 成人在线免费观看视视频| 久久精品99久久久香蕉| 亚洲二区中文字幕| 91精品久久久久久久久青青| 色偷偷av一区二区三区| 欧美日韩精品二区| 2021国产精品视频| 久久久精品影院| 一区二区三区在线播放欧美| 欧美性猛交xxxx黑人猛交| 最近2019中文字幕在线高清| www.久久久久| 亚洲电影免费观看| www高清在线视频日韩欧美| 91精品国产高清自在线看超| 精品动漫一区二区三区| 精品久久久久久久久久ntr影视| 国产精品第3页| 日韩一区视频在线| 亚洲精品视频二区| 黄色成人av网| 国产精品爱久久久久久久| 日韩在线视频线视频免费网站| 午夜精品久久久久久久99热| 日韩电影在线观看免费| 97精品久久久中文字幕免费| 日韩免费不卡av| 国产精品男人的天堂| 欧美精品亚州精品| 这里只有精品视频| 亚洲无亚洲人成网站77777| 国产精品91久久久| 国产欧美在线看| 久久99亚洲精品| 亚洲精品电影在线| 国产91在线视频| 97视频在线观看免费| 精品夜色国产国偷在线| 久久精品影视伊人网| 91最新在线免费观看| 亚洲欧洲一区二区三区在线观看| www.美女亚洲精品| 国产精品成人国产乱一区| 91精品成人久久| 按摩亚洲人久久| 欧美激情高清视频| 午夜精品久久久久久99热软件| 国产精品96久久久久久又黄又硬| 国产精品久久久久免费a∨| 中文字幕亚洲欧美日韩在线不卡| 亚洲最大激情中文字幕| 亚洲一区二区三区乱码aⅴ蜜桃女| 欧美激情一级二级| 亚洲一区中文字幕在线观看| 日韩精品在线视频美女| 欧美性理论片在线观看片免费| 清纯唯美日韩制服另类| 日韩欧美在线观看视频| 高清日韩电视剧大全免费播放在线观看| 日韩电影中文字幕| 国产成人97精品免费看片| 欧美性一区二区三区| 日韩的一区二区| 97在线视频免费观看| 国内成人精品视频| 日韩中文字幕在线精品| 久久精品99久久久香蕉| 国产精品久久久久久久久久久久| 久久久噜噜噜久久中文字免| 欧美性感美女h网站在线观看免费| 亚洲精品一区二区久| 午夜精品久久久99热福利| 中文字幕一区二区三区电影| 中文字幕精品一区二区精品| 国产视频综合在线| 欧美一区在线直播| 亚洲91精品在线观看| 亚洲一区二区福利| 色噜噜狠狠狠综合曰曰曰| 色综合伊人色综合网| 亚洲精品二三区| 国模视频一区二区三区| 欧美日韩国产中文精品字幕自在自线| 国产成人一区二区在线| 热99精品只有里视频精品| 成人免费看黄网站| 欧美性猛交xxxx乱大交蜜桃| 亚洲精品美女久久久久| 国内精品在线一区| 国产精品自产拍在线观| 国产精品扒开腿做爽爽爽视频| 亚洲国产精品久久久久秋霞蜜臀| 亚洲xxx大片| 亚洲激情第一页| 亚洲专区国产精品| 亚洲男人av电影| 国模精品系列视频| 亚洲激情在线观看视频免费| 伊人久久五月天| 欧美有码在线观看视频| 日韩成人黄色av| 国外成人在线视频| 91高清视频免费| 精品呦交小u女在线| 北条麻妃在线一区二区| 久久精品成人动漫| 亚洲天堂av电影| 国产精品一区二区久久| 国产精品入口日韩视频大尺度| 久久久亚洲精品视频|