成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

項目終于用上了插入式注解,真香!

開發 項目管理
我們為公司提供了一套通用的JAVA基礎組件包,組件包內有不同的模塊,比如熔斷模塊、負載均模塊、rpc模塊等等,這些模塊均會被打成jar包,然后發布到公司的內部代碼倉庫中,供其他人引入使用。

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

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

需求

我們為公司提供了一套通用的JAVA基礎組件包,組件包內有不同的模塊,比如熔斷模塊、負載均模塊、rpc模塊等等,這些模塊均會被打成jar包,然后發布到公司的內部代碼倉庫中,供其他人引入使用。

這份代碼會不斷的迭代,我們希望可以通過??promethus??來監控現在公司內使用各版本代碼庫的比例,希望達到的效果圖如下:

圖片

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

問題

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

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

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

@TrisceliVersion
public static final String version = "";

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

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

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

解決

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

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

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

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

圖片

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

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

@Documented
@Retention(RetentionPolicy.SOURCE) //只在編譯期有效,最終不會打進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<String> getSupportedAnnotationTypes() {
HashSet<String> set = new HashSet<>();
set.add(TrisceliVersion.class.getName()); // 支持解析的注解
return set;
}

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (TypeElement t : annotations) {
for (Element e : roundEnv.getElementsAnnotatedWith(t)) { // 獲取到給定注解的element(element可以是一個類、方法、包等)
// JCVariableDecl為字段/變量定義語法樹節點
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內的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,這里省略掉復雜的代碼,直接返回固定值
*/
return "v1.0.1";
}

定義好的處理器需要??SPI機制???被發現,所以需要定義??META.services??:

圖片

測試

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

圖片

這是Test類:

圖片

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

圖片

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

責任編輯:武曉燕 來源: 碼猿技術專欄
相關推薦

2024-09-14 09:59:04

2025-06-03 08:20:00

Feign微服務

2022-12-19 08:32:57

項目Feign框架

2025-02-18 14:08:14

2021-04-23 08:29:47

SkyWalking監控系統

2021-05-27 15:43:29

鴻蒙安卓和iOS

2023-09-14 15:15:36

2012-04-23 13:28:41

Voice AnsweSiriAppstore

2020-02-21 08:00:00

網頁廣告診斷

2020-04-09 08:29:50

編程語言事件驅動

2018-04-24 14:12:29

蘋果iPhone手機

2022-01-25 10:40:30

Windows 10微軟升級

2024-02-21 11:33:25

Serilog.NET日志庫

2020-07-23 10:51:29

NginxWebApache

2020-10-14 14:06:32

iPhone 12

2023-12-16 12:47:59

2018-07-24 15:23:18

2024-06-28 08:21:20

前端自動化部署

2022-03-24 07:51:27

seata分布式事務Java
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产高清精品一区二区三区 | 午夜视频在线观看视频 | 国产一区日韩在线 | 国产一级视频免费播放 | 精品成人69xx.xyz | 一区二区三区四区在线视频 | 精品三级在线观看 | 91国产在线视频在线 | 久久久久亚洲精品 | 谁有毛片 | 欧美5区| 色婷婷久久久亚洲一区二区三区 | 亚洲欧美国产精品久久 | 亚洲精品一区二区三区免 | 日本精品一区二区三区视频 | 国产精品高潮呻吟久久aⅴ码 | 99色在线| 国产一区高清 | 国产传媒毛片精品视频第一次 | 欧美456 | 亚洲国产精品区 | 色婷婷国产精品综合在线观看 | 999久久久免费精品国产 | 九九热这里 | 久久男人 | 日韩三 | 91传媒在线播放 | 国产在线一区观看 | 狠狠亚洲| h视频免费看 | 国产成人精品a视频一区www | 久久久久久国产精品免费免费狐狸 | 五月婷六月丁香 | 亚洲精品一区二区三区在线 | 欧美一区二区在线播放 | 久久久久久一区 | 日本黄色免费片 | 久久久999精品 | 少妇精品亚洲一区二区成人 | 97免费视频在线观看 | 久久国内 |