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

0x01 繼承AbstractProcessor抽象類


當(dāng)定義好Annotation注解后阶剑,接下來就需要一個(gè)注解處理器來處理我們的自定義注解了跃巡。實(shí)現(xiàn)Java Annotation一般需要繼承AbstractProcessor抽象類,并且重寫其四個(gè)方法來實(shí)現(xiàn)提取个扰,解析并處理自定義注解的邏輯如下:

class WondertwoProcessor extends AbstractProcessor {
    //返回注解處理器可處理的注解操作
    @Override
    public Set<String> getSupportedOptions() {
        return super.getSupportedOptions();
    }
    //得到注解處理器可以支持的注解類型
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return super.getSupportedAnnotationTypes();
    }
    //執(zhí)行一些初始化邏輯
    @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()方法才是掃描娘香,解析,處理注解的核心方法办龄,動(dòng)手實(shí)戰(zhàn)一下寫一個(gè)簡單的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編程思想》的同學(xué)肯定對上面的實(shí)例非常眼熟安接,書中對應(yīng)的實(shí)例也是提取非靜態(tài)公有方法生成接口源文件,但由于是JDK6.0標(biāo)準(zhǔn)已經(jīng)有很多API發(fā)生了很大的變化英融,本例基于JDK8盏檐!

可以看到我們只在process()方法中加入了處理注解,生成.java文件的邏輯驶悟,這里是的邏輯是根據(jù)自定義注解提取對應(yīng)類的非靜態(tài)public方法胡野,然后將抽取的非靜態(tài)共有方法拼接成對應(yīng)的接口!

0x03 實(shí)例探究:Android依賴注解庫ButterKnife


不會偷懶的程序員不是一個(gè)好程序員痕鳍,Android開發(fā)者對ButterKnife依賴注解庫一定耳熟能詳硫豆,當(dāng)我們UI布局中控件很多的時(shí)候ButterKnife無疑顯著提高了開發(fā)效率龙巨。

作為一個(gè)注解庫其實(shí)現(xiàn)的原理依然是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);
  }
}

如果想要進(jìn)一步了解ButteKnife掃描,解析汗茄,處理注解秸弛,生成Java代碼的每一部細(xì)節(jié),可以參考文章:淺析ButterKnife

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末剔难,一起剝皮案震驚了整個(gè)濱河市胆屿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌偶宫,老刑警劉巖非迹,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異纯趋,居然都是意外死亡憎兽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門吵冒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纯命,“玉大人,你說我怎么就攤上這事痹栖∫诠” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵揪阿,是天一觀的道長疗我。 經(jīng)常有香客問我,道長南捂,這世上最難降的妖魔是什么吴裤? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮溺健,結(jié)果婚禮上麦牺,老公的妹妹穿的比我還像新娘。我一直安慰自己鞭缭,他們只是感情好剖膳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著岭辣,像睡著了一般潮秘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上易结,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼搞动。 笑死躏精,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鹦肿。 我是一名探鬼主播矗烛,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼箩溃!你這毒婦竟也來了瞭吃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤涣旨,失蹤者是張志新(化名)和其女友劉穎歪架,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體霹陡,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡和蚪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烹棉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片攒霹。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖浆洗,靈堂內(nèi)的尸體忽然破棺而出催束,到底是詐尸還是另有隱情,我是刑警寧澤伏社,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布抠刺,位于F島的核電站,受9級特大地震影響洛口,放射性物質(zhì)發(fā)生泄漏矫付。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一第焰、第九天 我趴在偏房一處隱蔽的房頂上張望买优。 院中可真熱鬧,春花似錦挺举、人聲如沸杀赢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脂崔。三九已至,卻和暖如春梧喷,著一層夾襖步出監(jiān)牢的瞬間砌左,已是汗流浹背脖咐。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汇歹,地道東北人屁擅。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像产弹,于是被迫代替她去往敵國和親派歌。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 什么是注解(Annotation):Annotation(注解)就是Java提供了一種元程序中的元素關(guān)聯(lián)任何信息和...
    九尾喵的薛定諤閱讀 3,166評論 0 2
  • 本文章涉及代碼已放到github上annotation-study 1.Annotation為何而來 What:A...
    zlcook閱讀 29,158評論 15 116
  • Java 中的注解(Annotation) 是一個(gè)很方便的特性在Spring當(dāng)中得到了大量的應(yīng)用 , 我們也可以開...
    _秋天閱讀 8,918評論 3 22
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,104評論 25 707
  • 大概是自己出生在十月的緣故吧痰哨,最愛的季節(jié)一直是秋季胶果。江南的秋天來的晚,早已是立秋的時(shí)節(jié)斤斧,“秋老虎”卻還是霸占著秋早抠,...
    Yvonne不是我閱讀 485評論 2 1