日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

了解過lombok底層原理的都知道其使用的就是的插入式注解,那么今天筆者就以真實場景演示一下插入式注解的使用。

需求

我們?yōu)楣咎峁┝艘惶淄ㄓ玫腏AVA基礎(chǔ)組件包,組件包內(nèi)有不同的模塊,比如熔斷模塊、負(fù)載均模塊、rpc模塊等等,這些模塊均會被打成jar包,然后發(fā)布到公司的內(nèi)部代碼倉庫中,供其他人引入使用。

這份代碼會不斷的迭代,我們希望可以通過promethus來監(jiān)控現(xiàn)在公司內(nèi)使用各版本代碼庫的比例,希望達(dá)到的效果圖如下:


 

我們希望看到每一個版本的使用率,這有利于我們做版本兼容,必要的時候可以對古早版本使用者溯源。

問題

需求似乎很簡單,但真要獲取自身的jar版本號還是挺麻煩的,有個比較簡單但陰間的辦法,就是給每一個組件都加上當(dāng)前的jar版本號,寫到配置文件里或者直接設(shè)置成常量,這樣上報promethus時就可以直接獲取到j(luò)ar包版本號了,這個方法雖然可以解決問題,但每次迭代版本都要跟著改一遍所有組件包的版本號數(shù)據(jù),過于麻煩。

有沒有更好的解決辦法呢?比如我們可不可以在gradle打包構(gòu)建時拿到j(luò)ar包的版本號,然后注入到每個組件中去呢?就像lombok那樣,不需要寫get、set方法,只需要加個注解標(biāo)記就可以自動注入get、set方法。

比如我們可以給每個組件定義一個空常量,加上自定義的注解:

@TrisceliVersion public static final String version = "";

然后像lombok生成set/get方法那樣注入真正的版本號:

@TrisceliVersion public static final String version = "1.0.31-SNAPSHOT";

參考lombok的實現(xiàn),這其實是可以做到的,下面來看解決方案。

解決

java中解析一個注解的方式主要有兩種:編譯期掃描、運行期反射,這是lombok @Setter的實現(xiàn):

@Target({ElementType.FIELD, ElementType.TYPE}) @Retention(RetentionPolicy.SOURCE) public @interface Setter { // 略... }

可以看到@Setter的Retention是SOURCE類型的,也就是說這個注解只在編譯期有效,它甚至不會被編入class文件,所以lombok無疑是第一種解析方式,那用什么方式可以在編譯期就讓注解被解析到并執(zhí)行我們的解析代碼呢?答案就是定義插入式注解處理器(通過JSR-269提案定義的Pluggable Annotation Processing API實現(xiàn))

插入式注解處理器的觸發(fā)點如下圖所示:


 

也就是說插入式注解處理器可以幫助我們在編譯期修改抽象語法樹(AST)!所以現(xiàn)在我們只需要自定義一個這樣的處理器,然后其內(nèi)部拿到j(luò)ar版本信息(因為是編譯期,可以找到源碼的path,源碼里隨便搞個文件存放版本號,然后用java io讀取進(jìn)來即可),再將注解對應(yīng)語法樹上的常量值設(shè)置成jar包版本號,語法樹變了,最終生成的字節(jié)碼也會跟著變,這樣就實現(xiàn)了我們想在編譯期給常量version注入值的愿望。

自定義一個插入式注解處理器也很簡單,首先要將自己的注解定義出來:

@Documented @Retention(RetentionPolicy.SOURCE) //只在編譯期有效,最終不會打進(jìn)class文件中 @Target({ElementType.FIELD}) //僅允許作用于類屬性之上 public @interface TrisceliVersion { }

然后定義一個繼承了AbstractProcessor的處理器:

/** * {@link AbstractProcessor} 就屬于 Pluggable Annotation Processing API */ public class TrisceliVersionProcessor extends AbstractProcessor { private JavacTrees javacTrees; private TreeMaker treeMaker; private ProcessingEnvironment processingEnv; /** * 初始化處理器 * * @param processingEnv 提供了一系列的實用工具 */ @SneakyThrows @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.processingEnv = processingEnv; this.javacTrees = JavacTrees.instance(processingEnv); Context context = ((JavacProcessingEnvironment) processingEnv).getContext(); this.treeMaker = TreeMaker.instance(context); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latest(); } @Override public Set getSupportedAnnotationTypes() { HashSet set = new HashSet<>(); set.add(TrisceliVersion.class.getName()); // 支持解析的注解 return set; } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { for (TypeElement t : annotations) { for (Element e : roundEnv.getElementsAnnotatedWith(t)) { // 獲取到給定注解的element(element可以是一個類、方法、包等) // JCVariableDecl為字段/變量定義語法樹節(jié)點 JCTree.JCVariableDecl jcv = (JCTree.JCVariableDecl) javacTrees.getTree(e); String varType = jcv.vartype.type.toString(); if (!"java.lang.String".equals(varType)) { // 限定變量類型必須是String類型,否則拋異常 printErrorMessage(e, "Type '" + varType + "'" + " is not support."); } jcv.init = treeMaker.Literal(getVersion()); // 給這個字段賦值,也就是getVersion的返回值 } } return true; } /** * 利用processingEnv內(nèi)的Messager對象輸出一些日志 * * @param e element * @param m error message */ private void printErrorMessage(Element e, String m) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, m, e); } private String getVersion() { /** * 獲取version,這里省略掉復(fù)雜的代碼,直接返回固定值 */ return "v1.0.1"; }

定義好的處理器需要SPI機(jī)制被發(fā)現(xiàn),所以需要定義META.services:


 

測試

新建測試模塊,引入剛才寫好的代碼包:


 

這是Test類:


 

現(xiàn)在我們只需要讓gradle build一下,新得到的字節(jié)碼中該字段就有值了:


 

這只是插入式注解處理器功能的冰山一角,既然它可以通過修改抽象語法樹來控制生成的字節(jié)碼,那么自然就有人能充分利用其特性來實現(xiàn)一些很酷的插件,比如lombok,我們再也不用寫諸如set/get這種模板式的代碼了,只要我們足夠有創(chuàng)意,就可以讓基于這一套API實現(xiàn)的插件在功能上有很大的發(fā)揮空間。

 

原文:https://mp.weixin.qq.com/s/_VzwbsYUgbY53bc8d9AJEA

 

如果感覺本文對你有幫助,點贊關(guān)注支持一下

分享到:
標(biāo)簽:注解 插入
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達(dá)人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定