0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

插入式注解處理器使用記錄

jf_ro2CN3Fa ? 來(lái)源:碼猿技術(shù)專欄 ? 2023-01-31 16:47 ? 次閱讀

插入式注解處理器在《深入理解Java虛擬機(jī)》一書中有一些介紹(前端編譯篇有提到),但一直沒(méi)有機(jī)會(huì)使用,直到碰到這個(gè)需求,覺(jué)得再合適不過(guò)了,就簡(jiǎn)單用了一下,這里做個(gè)記錄。

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

需求

我們?yōu)?a target="_blank">公司提供了一套通用的JAVA基礎(chǔ)組件包,組件包內(nèi)有不同的模塊,比如熔斷模塊、負(fù)載均模塊、rpc模塊等等,這些模塊均會(huì)被打成jar包,然后發(fā)布到公司的內(nèi)部代碼倉(cāng)庫(kù)中,供其他人引入使用。

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

25f0eaae-8bee-11ed-bfe3-dac502259ad0.png

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

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

問(wèn)題

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

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

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

@TrisceliVersion
publicstaticfinalStringversion="";

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

@TrisceliVersion
publicstaticfinalStringversion="1.0.31-SNAPSHOT";

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

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

解決

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

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

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

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

25fff436-8bee-11ed-bfe3-dac502259ad0.png

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

自定義一個(gè)插入式注解處理器也很簡(jiǎn)單,首先要將自己的注解定義出來(lái):

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

然后定義一個(gè)繼承了AbstractProcessor的處理器:

/**
*{@linkAbstractProcessor}就屬于PluggableAnnotationProcessingAPI
*/
publicclassTrisceliVersionProcessorextendsAbstractProcessor{

privateJavacTreesjavacTrees;
privateTreeMakertreeMaker;
privateProcessingEnvironmentprocessingEnv;

/**
*初始化處理器
*
*@paramprocessingEnv提供了一系列的實(shí)用工具
*/
@SneakyThrows
@Override
publicsynchronizedvoidinit(ProcessingEnvironmentprocessingEnv){
super.init(processingEnv);
this.processingEnv=processingEnv;
this.javacTrees=JavacTrees.instance(processingEnv);
Contextcontext=((JavacProcessingEnvironment)processingEnv).getContext();
this.treeMaker=TreeMaker.instance(context);
}


@Override
publicSourceVersiongetSupportedSourceVersion(){
returnSourceVersion.latest();
}

@Override
publicSetgetSupportedAnnotationTypes(){
HashSetset=newHashSet<>();
set.add(TrisceliVersion.class.getName());//支持解析的注解
returnset;
}

@Override
publicbooleanprocess(Setannotations,RoundEnvironmentroundEnv){
for(TypeElementt:annotations){
for(Elemente:roundEnv.getElementsAnnotatedWith(t)){//獲取到給定注解的element(element可以是一個(gè)類、方法、包等)
//JCVariableDecl為字段/變量定義語(yǔ)法樹(shù)節(jié)點(diǎn)
JCTree.JCVariableDecljcv=(JCTree.JCVariableDecl)javacTrees.getTree(e);
StringvarType=jcv.vartype.type.toString();
if(!"java.lang.String".equals(varType)){//限定變量類型必須是String類型,否則拋異常
printErrorMessage(e,"Type'"+varType+"'"+"isnotsupport.");
}
jcv.init=treeMaker.Literal(getVersion());//給這個(gè)字段賦值,也就是getVersion的返回值
}
}
returntrue;
}

/**
*利用processingEnv內(nèi)的Messager對(duì)象輸出一些日志
*
*@parameelement
*@parammerrormessage
*/
privatevoidprintErrorMessage(Elemente,Stringm){
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,m,e);
}

privateStringgetVersion(){
/**
*獲取version,這里省略掉復(fù)雜的代碼,直接返回固定值
*/
return"v1.0.1";
}

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

260d4104-8bee-11ed-bfe3-dac502259ad0.png

測(cè)試

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

261da74c-8bee-11ed-bfe3-dac502259ad0.png

這是Test類:

262e5bbe-8bee-11ed-bfe3-dac502259ad0.png

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

264380a2-8bee-11ed-bfe3-dac502259ad0.png

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






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 處理器
    +關(guān)注

    關(guān)注

    68

    文章

    19395

    瀏覽量

    230691
  • JAVA
    +關(guān)注

    關(guān)注

    19

    文章

    2973

    瀏覽量

    104939
  • SPI
    SPI
    +關(guān)注

    關(guān)注

    17

    文章

    1721

    瀏覽量

    91920
  • RPC
    RPC
    +關(guān)注

    關(guān)注

    0

    文章

    111

    瀏覽量

    11548

原文標(biāo)題:項(xiàng)目終于用上了插入式注解,真香!

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    求推薦藍(lán)牙芯片!價(jià)格低于15元的,可以用作處理器使

    我想實(shí)現(xiàn)藍(lán)牙接收數(shù)據(jù),有數(shù)據(jù)處理器,有音頻處理器,又支持手機(jī)升級(jí)藍(lán)牙固件。有這樣的芯片嗎?能想到的方案有:1,藍(lán)牙芯片(可以手機(jī)升級(jí)固件,可以做處理器使用)+音頻解碼+功放:那么就要求藍(lán)牙芯片本身
    發(fā)表于 07-11 22:54

    寫入EEPROM的數(shù)據(jù)是否可供處理器使用?

    寫入EEPROM的數(shù)據(jù)是否可供處理器使用?如果我寫3個(gè)不同的代碼到EEPROM,處理器可以根據(jù)用戶需求選擇3個(gè)1嗎? 以上來(lái)自于百度翻譯 以下為原文Is the data written
    發(fā)表于 05-31 14:43

    嵌入處理器是什么

      嵌入處理器是嵌入系統(tǒng)的核心,是控制、輔助系統(tǒng)運(yùn)行的硬件單元。范圍極其廣闊,從最初的4位處理器,目前仍在大規(guī)模應(yīng)用的8位單片機(jī),到最新的受到廣泛青睞的32位,64位嵌入
    發(fā)表于 10-27 07:24

    嵌入處理器是什么

      嵌入處理器是嵌入系統(tǒng)的核心,是控制、輔助系統(tǒng)運(yùn)行的硬件單元。范圍極其廣闊,從最初的4位處理器,目前仍在大規(guī)模應(yīng)用的8位單片機(jī),到最新的受到廣泛青睞的32位,64位嵌入
    發(fā)表于 10-28 08:56

    HarmonyOS注解的使用方法分享

    ({ElementType.TYPE})//用于 接口、類、枚舉@Retention(RetentionPolicy.CLASS)//編譯階段public @interface CheckGetter {}2、編寫注解處理器新建
    發(fā)表于 03-28 14:04

    可以為S32E2處理器使用哪個(gè)管理程序嗎?

    我能知道我可以為 S32E2 處理器使用哪個(gè)管理程序嗎?
    發(fā)表于 03-16 07:07

    工控處理器和嵌入處理器誰(shuí)更優(yōu)?

    工控處理器和嵌入處理器誰(shuí)更優(yōu)?   工業(yè)計(jì)算機(jī)和所謂的嵌入相比,工業(yè)計(jì)算機(jī)
    發(fā)表于 02-11 14:50 ?562次閱讀

    什么是插入封裝

    什么是插入封裝 引腳插入封裝(Through-Hole Mount)。此封裝形式有引腳出來(lái),并將引腳直接插入印刷電路板(PWB)中,再
    發(fā)表于 03-04 11:02 ?3653次閱讀

    嵌入處理器選型

    嵌入處理器分類 處理器造型需考慮的因素 多處理器在復(fù)雜系統(tǒng)中的應(yīng)用
    發(fā)表于 02-28 11:57 ?64次下載
    嵌入<b class='flag-5'>式</b><b class='flag-5'>處理器</b>選型

    基于ARM 微處理器的故障記錄系統(tǒng)

    本文給出了在普通數(shù)據(jù)采集基礎(chǔ)上的故障記錄系統(tǒng)的設(shè)計(jì)方案, 利用ARM 微處理器實(shí)現(xiàn)了模擬信號(hào)較完整的故障波形記錄和開(kāi)關(guān)信號(hào)的事件順序記錄(SOE) 。對(duì)L PC2106ARM 系列
    發(fā)表于 06-02 17:11 ?57次下載
    基于ARM 微<b class='flag-5'>處理器</b>的故障<b class='flag-5'>記錄</b>系統(tǒng)

    Spring Web MVC注解

    RequestMapping注解的主要用途是將Web請(qǐng)求與請(qǐng)求處理類中的方法進(jìn)行映射。Spring MVC和Spring WebFlux都通過(guò)`RquestMappingHandlerMapping`和`RequestMappingHndlerAdapter`兩個(gè)類來(lái)提供
    的頭像 發(fā)表于 04-07 11:32 ?635次閱讀
    Spring Web MVC<b class='flag-5'>注解</b>

    Springboot常用注解合集

    前幾章,在系統(tǒng)啟動(dòng)類里面,都加入了此啟動(dòng)注解,此注解是個(gè)組合注解,包括了`@SpringBootConfiguration`、`@EnableAutoConfiguration`和`@ComponentScan`
    的頭像 發(fā)表于 04-07 14:27 ?758次閱讀
    Springboot常用<b class='flag-5'>注解</b>合集

    fido5100和fido5200 REM交換芯片與主機(jī)和網(wǎng)絡(luò)處理器使

    電子發(fā)燒友網(wǎng)站提供《fido5100和fido5200 REM交換芯片與主機(jī)和網(wǎng)絡(luò)處理器使用.pdf》資料免費(fèi)下載
    發(fā)表于 11-22 10:38 ?0次下載
    fido5100和fido5200 REM交換芯片與主機(jī)和網(wǎng)絡(luò)<b class='flag-5'>處理器使</b>用

    springmvc常用5種注解

    SpringMVC是一種基于Java的Web框架,使用注解可以更加方便靈活地開(kāi)發(fā)和管理控制器,實(shí)現(xiàn)請(qǐng)求的映射和處理。在SpringMVC中,有許多常用的注解,本文將詳細(xì)介紹其中的五種注解
    的頭像 發(fā)表于 11-22 16:51 ?940次閱讀

    什么是插入無(wú)線傳感器?作用是什么?

    。通過(guò)將這些數(shù)據(jù)傳輸?shù)綗o(wú)線網(wǎng)絡(luò),插入無(wú)線傳感器允許實(shí)時(shí)監(jiān)測(cè)和分析各種應(yīng)用領(lǐng)域的信息。 插入無(wú)線傳感器通常由以下幾個(gè)主要組件組成:傳感器、微處理器
    的頭像 發(fā)表于 12-12 15:04 ?731次閱讀