日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
項(xiàng)目終于用上了插入式注解,真香!

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

成都創(chuàng)新互聯(lián)專注于企業(yè)全網(wǎng)整合營(yíng)銷推廣、網(wǎng)站重做改版、定遠(yuǎn)網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5網(wǎng)站設(shè)計(jì)、商城系統(tǒng)網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為定遠(yuǎn)等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

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

需求

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

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

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

問題

需求似乎很簡(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è)方法雖然可以解決問題,但每次迭代版本都要跟著改一遍所有組件包的版本號(hào)數(shù)據(jù),過于麻煩。

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

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

@TrisceliVersion
public static final String version = "";

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

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

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

解決

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

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

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

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

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

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

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

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

/**
* {@link AbstractProcessor} 就屬于 Pluggable Annotation Processing API
*/
public class TrisceliVersionProcessor extends AbstractProcessor {

private JavacTrees javacTrees;
private TreeMaker treeMaker;
private ProcessingEnvironment processingEnv;

/**
* 初始化處理器
*
* @param processingEnv 提供了一系列的實(shí)用工具
*/
@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可以是一個(gè)類、方法、包等)
// JCVariableDecl為字段/變量定義語(yǔ)法樹節(jié)點(diǎn)
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()); // 給這個(gè)字段賦值,也就是getVersion的返回值
}
}
return true;
}

/**
* 利用processingEnv內(nèi)的Messager對(duì)象輸出一些日志
*
* @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??:

測(cè)試

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

這是Test類:

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

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


分享名稱:項(xiàng)目終于用上了插入式注解,真香!
本文URL:http://www.5511xx.com/article/cophich.html