最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java注解處理器學(xué)習(xí)之編譯時處理的注解詳析

 更新時間:2018年05月11日 11:35:18   作者:ganchuanpu  
編譯時注解相信對每一個java開發(fā)者來說都不陌生,下面這篇文章主要給大家介紹了關(guān)于Java注解處理器學(xué)習(xí)之編譯時處理的注解的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧

1. 一些基本概念

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

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

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

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

Annotation processing是在編譯階段執(zhí)行的,它的原理就是讀入Java源代碼,解析注解,然后生成新的Java代碼。新生成的Java代碼最后被編譯成Java字節(jié)碼,注解解析器(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) :所有的注解處理器類都必須有一個無參構(gòu)造函數(shù)。然而,有一個特殊的方法init(),它會被注解處理工具調(diào)用,以ProcessingEnvironment作為參數(shù)。ProcessingEnvironment 提供了一些實用的工具類Elements, Types和Filer。我們在后面將會使用到它們。

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

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

getSupportedSourceVersion() : 用來指定你使用的 java 版本。通常你應(yīng)該返回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。

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

3. 注冊你的處理器

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

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

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

com.example.MyProcess

com.example.AnotherProcess

4. 例子:工廠模式

我們要解決的問題是:我們要實現(xiàn)一個 pizza 店,這個 pizza 店提供給顧客兩種 pizza (Margherita 和 Calzone),還有甜點(diǎn) 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注解應(yīng)用到這些類上吧:

@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注解應(yīng)用到Meal接口上?答案是不行,因為注解是不能被繼承的。即在class X上有注解,class Y extends X,那么class Y是不會繼承class X上的注解的。在我們編寫處理器之前,需要明確幾點(diǎn)規(guī)則:

  • 只有類能夠被@Factory注解,因為接口和虛類是不能通過new操作符實例化的。
  • 被@Factory注解的類必須提供一個默認(rèn)的無參構(gòu)造函數(shù)。否則,我們不能實例化一個對象。
  • 被@Factory注解的類必須直接繼承或者間接繼承type指定的類型。(或者實現(xiàn)它,如果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注解將被這個處理器處理。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Spring Boot利用@Async異步調(diào)用:使用Future及定義超時詳解

    Spring Boot利用@Async異步調(diào)用:使用Future及定義超時詳解

    這篇文章主要給大家介紹了關(guān)于Spring Boot利用@Async異步調(diào)用:使用Future及定義超時的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2018-05-05
  • Before和BeforeClass的區(qū)別及說明

    Before和BeforeClass的區(qū)別及說明

    這篇文章主要介紹了Before和BeforeClass的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • java讀取配置文件自定義字段(yml、properties)

    java讀取配置文件自定義字段(yml、properties)

    本文主要介紹了java讀取配置文件自定義字段(yml、properties),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • Session過期后實現(xiàn)自動跳轉(zhuǎn)登錄頁面

    Session過期后實現(xiàn)自動跳轉(zhuǎn)登錄頁面

    這篇文章主要介紹了Session過期后實現(xiàn)自動跳轉(zhuǎn)登錄頁面,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-12-12
  • SpringBoot電腦商城項目刪除收貨地址的實現(xiàn)方法

    SpringBoot電腦商城項目刪除收貨地址的實現(xiàn)方法

    這篇文章主要介紹了SpringBoot項目--電腦商城刪除收貨地址功能實現(xiàn),本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09
  • java中FileOutputStream中文亂碼問題解決辦法

    java中FileOutputStream中文亂碼問題解決辦法

    這篇文章主要介紹了java中FileOutputStream中文亂碼問題解決辦法的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • MyBatis環(huán)境資源配置實現(xiàn)代碼詳解

    MyBatis環(huán)境資源配置實現(xiàn)代碼詳解

    這篇文章主要介紹了MyBatis環(huán)境資源配置實現(xiàn)代碼解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-08-08
  • 淺談異常結(jié)構(gòu)圖、編譯期異常和運(yùn)行期異常的區(qū)別

    淺談異常結(jié)構(gòu)圖、編譯期異常和運(yùn)行期異常的區(qū)別

    下面小編就為大家?guī)硪黄獪\談異常結(jié)構(gòu)圖、編譯期異常和運(yùn)行期異常的區(qū)別。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-09-09
  • 利用Spring框架為自己的校園卡充值(推薦)

    利用Spring框架為自己的校園卡充值(推薦)

    這篇文章主要介紹了利用Spring框架為自己的校園卡充值,本次實驗主要運(yùn)用了Spring的控制反轉(zhuǎn)(IOC)和依賴注入(DI)等知識,通過利用Spring框架編寫Java程序,實現(xiàn)學(xué)生卡的單次充值,如需對學(xué)生卡進(jìn)行多次充值,可將部分代碼修改即可實現(xiàn),需要的朋友可以參考下
    2022-10-10
  • 一文詳細(xì)講解Java的父子繼承、方法的重寫與super關(guān)鍵字

    一文詳細(xì)講解Java的父子繼承、方法的重寫與super關(guān)鍵字

    Java中繼承是通過extends關(guān)鍵字實現(xiàn),幫助減少代碼重復(fù),提高復(fù)用性,子類繼承父類的屬性和方法,但私有成員除外,方法重寫(override)使子類改造父類方法以適應(yīng)新功能,這篇文章主要介紹了Java的父子繼承、方法的重寫與super關(guān)鍵字的相關(guān)資料,需要的朋友可以參考下
    2024-10-10

最新評論

龙陵县| 会昌县| 北碚区| 定陶县| 正蓝旗| 故城县| 丰城市| 威海市| 博客| 颍上县| 比如县| 扬州市| 南漳县| 盘山县| 松阳县| 蓬溪县| 昭通市| 荥阳市| 平湖市| 德阳市| 柞水县| 长子县| 沂南县| 集安市| 无为县| 望都县| 石泉县| 江口县| 沙坪坝区| 石屏县| 民和| 凤阳县| 陇南市| 盐津县| 苏州市| 南陵县| 志丹县| 广东省| 甘南县| 景宁| 福泉市|