1. 程式人生 > >註解提高篇:自定義註解處理器(APT)

註解提高篇:自定義註解處理器(APT)

## 0x01 繼承AbstractProcessor抽象類

當定義好Annotation註解後,接下來就需要一個註解處理器來處理我們的自定義註解了。實現Java Annotation一般需要繼承AbstractProcessor抽象類,並且重寫其四個方法來實現提取,解析並處理自定義註解的邏輯如下:

class WondertwoProcessor extends AbstractProcessor {
    //返回註解處理器可處理的註解操作
    @Override
    public Set<String> getSupportedOptions() {
        return super.getSupportedOptions();
    }
    //得到註解處理器可以支援的註解型別
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }
    //執行一些初始化邏輯
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
    }
    //核心方法,掃描,解析並處理自定義註解,生成***.java檔案
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        return false;
    }
}

## 0x02 重寫核心方法process()

由上可知process()方法才是掃描,解析,處理註解的核心方法,動手實戰一下寫一個簡單的WondertwoProcessor來提取自定義註解@CustomizeInterface,然後藉助JavaPoet生成Java介面檔案。

/**
 * 自定義註解處理器,將類中public方法提取為介面方法(不含static方法)
 * {
 *     Exec: apt -factory annotation3.WondertwoFactory
 *     ProvinceDefiner.java -s ../annotaion3
 * }
 * Created by wondertwo on 2016/10/18.
 */
class WondertwoProcessor extends AbstractProcessor {
    private ProcessingEnvironment envir;

    public WondertwoProcessor(ProcessingEnvironment env) {
        this.envir = env;
    }

    @Override
    public Set<String> getSupportedOptions() {
        return super.getSupportedOptions();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }

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

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement typeEle : annotations) {
            WondertwoInterface wondertwoInterface = typeEle.getAnnotation(WondertwoInterface.class);
            if (wondertwoInterface == null) break;

            Class clazz = typeEle.getClass();
            if (clazz.getDeclaredMethods().length > 0) {
                try {
                    if (typeEle.getModifiers().contains(Modifier.PUBLIC)
                            && !typeEle.getModifiers().contains(Modifier.STATIC)) {
                        PrintWriter writer = (PrintWriter) envir.getFiler()
                                .createSourceFile(wondertwoInterface.value());
                        writer.println("package " + clazz.getPackage().getName() + ";");
                        writer.println("public interface " + wondertwoInterface.value() + " {");
                        for (Method method : clazz.getDeclaredMethods()) {
                            writer.print("    public ");
                            writer.print(method.getReturnType() + " ");
                            writer.print(method.getName() + " (");
                            int i = 0;
                            for (TypeParameterElement parameter : typeEle.getTypeParameters()) {
                                writer.print(parameter.asType() + " " + parameter.getSimpleName());
                                if (++i < typeEle.getTypeParameters().size())
                                    writer.print(", ");
                            }
                            writer.println(");");
                        }
                        writer.println("}");
                        writer.close();
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return true;
    }
}

看過《Java程式設計思想》的同學肯定對上面的例項非常眼熟,書中對應的例項也是提取非靜態公有方法生成介面原始檔,但由於是JDK6.0標準已經有很多API發生了很大的變化,本例基於JDK8!

可以看到我們只在process()方法中加入了處理註解,生成.java檔案的邏輯,這裡是的邏輯是根據自定義註解提取對應類的非靜態public方法,然後將抽取的非靜態共有方法拼接成對應的介面!

## 0x03 例項探究:Android依賴註解庫ButterKnife

不會偷懶的程式設計師不是一個好程式設計師,Android開發者對ButterKnife依賴註解庫一定耳熟能詳,當我們UI佈局中控制元件很多的時候ButterKnife無疑顯著提高了開發效率。

作為一個註解庫其實現的原理依然是Java Annotation的方式,我們在Github翻出ButterKnife原始碼檔案,找到其核心類——註解處理類ButterKnifeProcessor.java,原始碼較長刪減後如下:

public final class ButterKnifeProcessor extends AbstractProcessor {
  @Override public synchronized void init(ProcessingEnvironment env) {
    super.init(env);
    elementUtils = env.getElementUtils();
    typeUtils = env.getTypeUtils();
    filer = env.getFiler();
  }
  @Override public Set<String> getSupportedAnnotationTypes() {
    Set<String> types = new LinkedHashSet<String>();
    types.add(Bind.class.getCanonicalName());
    for (Class<? extends Annotation> listener : LISTENERS) {
      types.add(listener.getCanonicalName());
    }
    types.add(BindBool.class.getCanonicalName());
    types.add(BindColor.class.getCanonicalName());
    types.add(BindDimen.class.getCanonicalName());
    types.add(BindDrawable.class.getCanonicalName());
    types.add(BindInt.class.getCanonicalName());
    types.add(BindString.class.getCanonicalName());
    return types;
  }
  @Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
    Map<TypeElement, BindingClass> targetClassMap = findAndParseTargets(env);
    for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) {
      TypeElement typeElement = entry.getKey();
      BindingClass bindingClass = entry.getValue();
      try {
        JavaFileObject jfo = filer.createSourceFile(bindingClass.getFqcn(), typeElement);
        Writer writer = jfo.openWriter();
        writer.write(bindingClass.brewJava());
        writer.flush();
        writer.close();
      } catch (IOException e) {
        error(typeElement, "Unable to write view binder for type %s: %s", typeElement,
            e.getMessage());
      }
    }
    return true;
  }
  @Override public Set<String> getSupportedOptions() {
    return Collections.singleton(OPTION_SDK_INT);
  }
}

如果想要進一步瞭解ButteKnife掃描,解析,處理註解,生成Java程式碼的每一部細節,可以參考文章:淺析ButterKnife

相關推薦

註解提高定義註解處理器APT

## 0x01 繼承AbstractProcessor抽象類 當定義好Annotation註解後,接下來就需要一個註解處理器來處理我們的自定義註解了。實現Java Annotation一般需要繼承AbstractProcessor抽象類,並且重寫其四個方法來實現提取,解析並處理自定義註解的邏輯如下: cla

註解基礎定義Java Annotation

## 寫在前面 JDK5增加了對Annotation(註解)的支援,Annotation是程式碼裡的特殊標記,這些標記可以在編譯,類載入和執行時被讀取讀取出來,並執行相應的處理和操作!比如在不改變程式邏輯的情況下,開發人員可以在程式碼中嵌入一些補充資訊,程式碼分析和開發部署工具APT(AnnotationP

Android定義控制元件系列二定義開關按鈕

這一次我們將會實現一個完整純粹的自定義控制元件,而不是像之前的組合控制元件一樣,拿系統的控制元件來實現;計劃分為三部分:自定義控制元件的基本部分,和自定義控制元件的自定義屬性; 下面就開始第一部分的編寫,本次以一個定義的開關按鈕為例,下面就開始吧: 先看看效果,一個點選開

Android註解定義註解之原始碼註解

首先如果你對註解沒有太多瞭解,建議先看一下我之前的兩篇部落格 Android註解:Java註解 Android註解:Android 常用註解   這兩篇部落格都詳細介紹了關於Android註解的一些基礎知識。這是Android自定義註解的第一篇,原始碼註解。A

2 手寫實現SpringMVC,第二節定義註解及反射賦值

還是回到最終要實現的效果。 可以發現,這裡面使用了大量的自定義註解,並且還有autuwire的屬性也需要被賦值(Spring的IOC功能)。 先來建立自定義註解 注意,根據不同的註解使用的範圍來定義@Target,譬如Controller,Service能註解到類,R

Java註解定義註解示例,利用反射進行解析

        Java註解能夠提供程式碼的相關資訊,同時對於所註解的程式碼結構又沒有直接影響。在這篇教程中,我們將學習Java註解,如何編寫自定義註解,註解的使用,以及如何使用反射解析註解。        註解是Java 1.5引入的,目前已被廣泛應用於各種Java框

2定義註解日誌脫敏半原創

import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import j

Android進階必學定義註解之動態代理

自定義註解是Android進階的必學知識,從現在起我講為大家帶來四篇文章,讓大家徹底學會自定義註解 靜態代理大家都明白,就是相當於包裝了一次,在包裝這一次的時候可以加一些業務邏輯。同樣靜態代理的特點是一個介面對應一個代理類,當然委託類可以多個。 靜態

Spring Boot定義註解掃描器 Spring Boot定義註解掃描器

原 Spring Boot自定義註解掃描器 2018年08月15日 14:37:52 Dongguabai 閱讀數:502

【java定義註解2】java定義註解結合Spring AOP

      承接上一篇,註解應用於屬性,本篇定義了一個用於方法的註解,結合Spring AOP 實現 切面程式設計。       以下demo演示使用了SpringBoot,與SSM中使用方式大致相同,效果如下: 1、自定義註解(用

【java定義註解1】java定義註解-屬性

        關於自定義註解,以前專案種應用的不多,最近看新專案過程中發現了挺多自定義註解相關內容,使用起來比較巧妙,於是 總結了兩種方式,記錄如下:         第一種:結合反射進行屬性注入,程式碼如下:

精通Spring Boot——第十八定義認證流程

前兩篇簡單介紹了一下使用Spring Security 使用Http Basic登入,以及Spring Security如何自定義登入邏輯。這篇文章主要介紹如何使用handler來定義認證相關的流程。 先做一些自定義的操作,如配置自定義登入頁,配置登入請求URL等。 當我們使用Spring Security時

java註解詳解和定義註解

本文首先介紹了註解的基本概念和JDK內建的標準註解,然後介紹瞭如何自定義註解,最後給出了自定義註解的例子。 一、註解的基本概念 Java 註解就像修飾符一樣,可以用於從java程式碼中抽取文件、跟蹤程式碼中的依賴性或者在編譯時做檢查。註解可以被應用在包、類、

Spring註解的原理與定義註解的實現

本文只是用於記錄個人在學習annotation過程中的心德,所以知識面不太完善。 1、註解的基本概念     Java 的annotation提供的是一種類似於註釋的機制,註解本身不做任何事,好比一個配置或者說一個標記。用於包、型別、構造方法、方法、成員變數、引數及本地變數

MySQL 第八定義函式、儲存過程、遊標

本篇內容由猿道教育的課程內容整理而來。 我把MySQL的內容整理成9篇部落格,學完這9篇部落格雖不能說能成為大神,但是應付一般中小企業的開發已經足夠了,有疑問或建議的歡迎留言討論。 自定義函式 一、函式的概念與定義 1、理解函式 函式可以看作是

Java註解詳解,定義註解,利用反射解析註解

概要 這篇文章將會帶領你瞭解Java註解,註解的使用,註解的解析,利用反射解析執行時註解,相信有一定Java基礎的小夥伴一定會接觸大量的註解,Spring , Hibernate , MyBatis等著名的框架也有很多關於註解方面的應用,對於註解的使用小夥伴們

java註解簡單講解以及定義註解例子

註解(Annotation) jdk5定義了4個標準的元註解。除了元註解,還有其它幫我們定義好的註解如@SuppressWarnings  當然我們也可以自定義註解 @Target, @Retention, @Documented, @Inherited @Retent

Spring AOP 基於註解實現日誌記錄+定義註解

一、寫一個自定義註解        註解中包括配置方法所在模組名稱,以及功能名稱,當然我們在註解裡可以自定義。import java.lang.annotation.Documented; import java.lang.annotation.ElementType; im

Android特效第三定義Gallery實戰(仿網易) .

Android系統提供了一個Gallery畫廊控制元件,在專案很多時候都會用到Gallery,比如新浪首頁的廣告,網易看客戶端首頁等隨處可見,今天我自己定義了一個仿網易的Gallery與大家共享。      首先請看效果圖:                        

Web開發之-JSP學習總結-第四定義標籤總結

一、自定義標籤開發步驟—以高仿<c:if test=""></c:if>標籤為例 1、編寫一個普通的java類,繼承SimpleTagSupport類,叫標籤處理器類。並且覆蓋doTag方法 /** * 標籤處理器類 * 1)繼承