欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java注解處理器學(xué)習(xí)在編譯期修改語法樹教程

 更新時間:2022年09月20日 10:02:33   作者:煩啦  
這篇文章主要為大家介紹了java注解處理器學(xué)習(xí)在編譯期修改語法樹教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

從需求說起

由于相關(guān)政策,最近公司安全部要求各系統(tǒng)在rpc接口調(diào)用的交互過程中把相應(yīng)的參數(shù)及結(jié)果以相應(yīng)的格式發(fā)送到安全部統(tǒng)一記錄,例如參數(shù)或結(jié)果含手機(jī)號和郵箱則格式如:“mail:axxx@126.com,phone:183xxxx1967”,其它系統(tǒng)信息等先忽略。

以便在數(shù)據(jù)泄露時可據(jù)此分析出數(shù)據(jù)的泄露源頭,以及若有黑客攻克有些接口時公司能有跡可循。

總體架構(gòu)是各個接口把入?yún)⒑徒Y(jié)果打印日志,然后由統(tǒng)一的日志收集器收集日志通過mq發(fā)送到安全部。這樣每個系統(tǒng)只用在接口中添加參數(shù)和結(jié)果的打日志代碼。

添加打印日志代碼的方案

第一種方案,硬編碼

即直接在接口中編寫打印日志的代碼。這種工作量太大,公司各個部門,以往積累了眾多的項目,這樣改造的工作量太大。

第二種方案,AOP

利用aop框架,在切面類中打印日志??梢允褂胹pring 支持的aop功能或其他aop框架。

這個方案應(yīng)該來說改動及工作量都大大降低,公司也是采用的這種方案。但是其弊端也很明顯,

一、是對框架的依賴(如用spring aop的話則非spring項目則不適用)

二、就是不同的項目或接口,入?yún)⒒蚪Y(jié)果變量名不同,如手機(jī)號:有的叫mobilePhone, 有的叫telephone等;但打印日志時要統(tǒng)一打印,如:phone:183xxxx1967; 所以要在參數(shù)上加注解,以表明打印日志時的名稱。這個重復(fù)工作量也不小。

第三種方案,修改class文件

針對第二種方案的弊端,我設(shè)計出這第三種方案。

利用相關(guān)技術(shù),直接修改class文件,在接口中添加打印日志的字節(jié)碼。例如Javassist,asm等技術(shù)。

通過調(diào)研,在編譯期通過修改語法樹來達(dá)到修改class文件的效果,這種對用戶來說完全透明,不依賴任何框架。針對弊端二則發(fā)明名稱分析模塊,讓程序自動分析出參數(shù)的含義,從而避免了手工添加注解的麻煩。

下面就具體說明第三種方案的實現(xiàn):

利用JDK的注解處理器,可在編譯期間對注解處理,可以讀取、修改、添加抽象語法樹中的任意元素。

注解處理器是JDK1.6開始提供的功能,利用注解處理器可以干涉編譯器的行為,只要有足夠的創(chuàng)意,可以利用注解處理器實現(xiàn)許多原本只能在編碼中完成的事情。

注解處理器的用法:

1、實現(xiàn)AbstractProcessor

實現(xiàn)init和process方法

顧名思義,init是完成一些初始化工作;process完成具體的邏輯處理。后邊會有具體的例子說明。

2、添加注解

@SupportedAnnotationTypes 指定此注解處理器支持的注解,可用*指定所有注解

@SupportedSourceVersion 指定支持的java的版本

注解實例:

在process方法中可獲取到注解有@Safety的類和方法。

Set<? extends Element> set = roundEnv.getElementsAnnotatedWith(Safety.class);

然后可遍歷方法,獲取到方法的入?yún)ⅲ治鋈雲(yún)?,給方法的語法樹添加打印參數(shù)日志的代碼。對于方法的結(jié)果是同樣的道理。

關(guān)于對語法樹的操作,網(wǎng)上資料相對較少及較為片段,介紹起來篇幅略長,故放在最后面進(jìn)行介紹。

名稱分析模塊的思想及設(shè)計

剩下的一個關(guān)鍵問題是如何把不同的參數(shù)名統(tǒng)一成打印日志時的名稱,例如參數(shù)名為mobilePhone或telephone,但要打印的是phone。如果在注解屬性中指定的話,通過注解可以獲取到,但是當(dāng)接口或參數(shù)很多的情況下也是一件重復(fù)性的力氣活。

故我設(shè)計出一種不讓開發(fā)人員手動指定名稱的方案,既對老的項目修改的少,又減輕開發(fā)人員的工作量,對新項目的應(yīng)用也是高效率的。

如圖:

詞庫存儲(可用類的靜態(tài)字段存儲)需要打印的日志名稱及其對應(yīng)的詞根及單詞。如:

上圖紅框為打印日志時要打印的名稱。綠框中為詞根及單詞:如若業(yè)務(wù)參數(shù)有用postbox作為郵箱變量名的則也可把postbox加入到mail的詞庫中。

這樣當(dāng)業(yè)務(wù)參數(shù)為mobilePhone或telephone時,名稱分析模塊能夠分析出參數(shù)名包含phone詞根,從而得到對應(yīng)的打印日志名“phone”;這就要求業(yè)務(wù)參數(shù)的名義要有具體的含義,不能隨便字母組合沒有含義的詞語,這應(yīng)該也是每個公司開發(fā)時的基本要求。

這里只是舉例一個簡單的可行性方案,名稱分析模塊也可利用AI技術(shù),根據(jù)輸入的變量名利用智能技術(shù)分析出此變量名的含義。

語法樹的操作:

下面對語法樹的操作進(jìn)行詳細(xì)的說明,這里需要提到三個類:

JavacTrees 提供了待處理的抽象語法樹TreeMaker 封裝了創(chuàng)建AST節(jié)點的一些方法Names 提供了創(chuàng)建標(biāo)識符的方法

可在init方法中對這三個類初始化,以便在process方法中利用它們對語法樹進(jìn)行操作。如圖:

AST(抽象語法樹)是一種用來描述程序代碼語法結(jié)構(gòu)的樹形表示方式,語法樹的每一個節(jié)點都代表著程序代碼中的一個語法結(jié)構(gòu),例如包、類型、修飾符、運算符、接口、返回值,甚至代碼注釋等都可以是一個語法結(jié)構(gòu)。

JCTree的介紹

JCTree

JCTree是語法樹元素的基類。

如上圖,它包含兩個屬性,

字段type表示語法結(jié)構(gòu)的類型

字段pos用于指明當(dāng)前語法樹節(jié)點(JCTree)在語法樹中的位置,因此我們不能直接用new關(guān)鍵字來創(chuàng)建語法樹節(jié)點,即使創(chuàng)建了也沒有意義,而要用TreeMaker來進(jìn)行操作。

重點介紹幾個JCTree的子類:

JCStatement:聲明語法樹節(jié)點,常見的子類如下 JCBlock:語句塊JCReturn:return語句JCClassDecl:類定義JCVariableDecl:字段/變量定義JCIf: if語句

   2.JCMethodDecl:方法定義語法樹節(jié)點

   3.JCModifiers:訪問標(biāo)志語法樹節(jié)點

   4.JCExpression:表達(dá)式語法樹節(jié)點,常見的子類如下

JCAssign:賦值語句JCAssignOp:+=JCIdent:標(biāo)識符,可以是變量,類型,關(guān)鍵字等等JCLiteral: 字面量表達(dá)式,如123, “string”等JCBinary:二元操作符

JCTree的子類很多,大部分可以從字面上看出其意義

如上圖,在jdk1.8.0_65里JCTree有58個子類。

下面具體介紹對語法樹的操作。

TreeMaker

TreeMaker創(chuàng)建語法樹節(jié)點的所有方法,創(chuàng)建時會為創(chuàng)建出來的JCTree設(shè)置pos字段,所以必須用上下文相關(guān)的TreeMaker對象來創(chuàng)建語法樹節(jié)點,而不能直接new語法樹節(jié)點。

TreeMaker.Modifiers

該方法用于創(chuàng)建訪問標(biāo)志語法樹節(jié)點(JCModifiers),源碼如下:

public JCModifiers Modifiers(long flags, List<JCAnnotation> annotations) {
        JCModifiers tree = new JCModifiers(flags, annotations);
        boolean noFlags = (flags & Flags.StandardFlags) == 0;
        tree.pos = (noFlags && annotations.isEmpty()) ? Position.NOPOS : pos;
        return tree;
    }
    public JCModifiers Modifiers(long flags) {
        return Modifiers(flags, List.<JCAnnotation>nil());
    }

參數(shù)解釋:

flags:訪問標(biāo)志

annotations:注解列表

其中flags可以用枚舉類型com.sun.tools.javac.code.Flags,且支持相加(Flags的值按二進(jìn)制設(shè)計),如圖:

用法示例:treeMaker.Modifiers(Flags.PUBLIC + Flags.STATIC + Flags.FINAL);

TreeMaker.ClassDef

該方法用于創(chuàng)建類定義語法樹節(jié)點(JCClassDef),源碼如下:

public JCClassDecl ClassDef(JCModifiers mods,
                                Name name,
                                List<JCTypeParameter> typarams,
                                JCTree extending,
                                List<JCExpression> implementing,
                                List<JCTree> defs)
    {
        JCClassDecl tree = new JCClassDecl(mods,
                                     name,
                                     typarams,
                                     extending,
                                     implementing,
                                     defs,
                                     null);
        tree.pos = pos;
        return tree;
    }

參數(shù)解釋:

mods:訪問標(biāo)志

name:方法名

restype:返回類型

typarams:泛型參數(shù)列表

params:參數(shù)列表

thrown:異常聲明列表

body:方法體

defaultValue:默認(rèn)方法(可能是interface中的那個default)

m:方法符號

mtype:方法類型。包含多種類型,泛型參數(shù)類型、方法參數(shù)類型,異常參數(shù)類型、返回參數(shù)類型

TreeMaker.MethodDef

用于創(chuàng)建方法定義語法樹節(jié)點(JCMethodDecl),源碼如下:

public JCMethodDecl MethodDef(JCModifiers mods,
                               Name name,
                               JCExpression restype,
                               List<JCTypeParameter> typarams,
                               List<JCVariableDecl> params,
                               List<JCExpression> thrown,
                               JCBlock body,
                               JCExpression defaultValue)
    {
        JCMethodDecl tree = new JCMethodDecl(mods,
                                       name,
                                       restype,
                                       typarams,
                                       params,
                                       thrown,
                                       body,
                                       defaultValue,
                                       null);
        tree.pos = pos;
        return tree;
    }

參數(shù)解釋:

mods:訪問標(biāo)志

name:方法名

restype:返回類型

typarams:泛型參數(shù)列表

params:參數(shù)列表

thrown:異常聲明列表

body:方法體

defaultValue:默認(rèn)方法(可能是interface中的那個default)

m:方法符號

mtype:方法類型。包含多種類型,泛型參數(shù)類型、方法參數(shù)類型,異常參數(shù)類型、返回參數(shù)類型

TreeMaker.VarDef

用于創(chuàng)建字段/變量定義語法樹節(jié)點(JCVariableDecl),源碼如下:

public JCVariableDecl VarDef(JCModifiers mods, Name name, JCExpression vartype, JCExpression init) {
        JCVariableDecl tree = new JCVariableDecl(mods, name, vartype, init, null);
        tree.pos = pos;
        return tree;
    }

參數(shù)解釋:

mods:訪問標(biāo)志

vartype:類型

init:初始化語句

v:變量符號

TreeMaker.Ident

用于創(chuàng)建標(biāo)識符語法樹節(jié)點(JCIdent),源碼如下:

public JCIdent Ident(Name name) {
        JCIdent tree = new JCIdent(name, null);
        tree.pos = pos;
        return tree;
}
public JCIdent Ident(Symbol sym) {
        return (JCIdent)new JCIdent((sym.name != names.empty)
                                ? sym.name
                                : sym.flatName(), sym)
            .setPos(pos)
            .setType(sym.type);
}
public JCExpression Ident(JCVariableDecl param) {
        return Ident(param.sym);
}

TreeMaker.Return

用于創(chuàng)建return語句語法樹節(jié)點(JCReturn)

TreeMaker.NewClass

用于創(chuàng)建new語句語法樹節(jié)點(JCNewClass),源碼如下:

public JCNewClass NewClass(JCExpression encl,
                             List<JCExpression> typeargs,
                             JCExpression clazz,
                             List<JCExpression> args,
                             JCClassDecl def)
    {
        JCNewClass tree = new JCNewClass(encl, typeargs, clazz, args, def);
        tree.pos = pos;
        return tree;
    }

參數(shù)解釋:

encl:不太明白此參數(shù)含義

typeargs:參數(shù)類型列表

clazz:待創(chuàng)建對象的類型

args:參數(shù)列表

def:類定義

TreeMaker.Select

用于創(chuàng)建域訪問/方法訪問(當(dāng)是方法訪問時,常和方法的調(diào)用TreeMaker.Apply一起使用語法樹節(jié)點(JCFieldAccess)

public JCFieldAccess Select(JCExpression selected,
    Name selector) 
{
        JCFieldAccess tree = new JCFieldAccess(selected, selector, null);
        tree.pos = pos;
        return tree;
}
public JCExpression Select(JCExpression base,
    Symbol sym) {
        return new JCFieldAccess(base, sym.name, sym).setPos(pos).setType(sym.type);
}

參數(shù)解釋:

selected:.運算符左邊的表達(dá)式

selector:.運算符右邊的名字

TreeMaker.Apply

用于創(chuàng)建方法調(diào)用語法樹節(jié)點(JCMethodInvocation),源碼如下:

public JCMethodInvocation Apply(List<JCExpression> typeargs,
                       JCExpression fn,
                       List<JCExpression> args)
    {
        JCMethodInvocation tree = new JCMethodInvocation(typeargs, fn, args);
        tree.pos = pos;
        return tree;
    }

參數(shù)解釋:

typeargs:參數(shù)類型列表

fn:調(diào)用語句

args:參數(shù)列表

TreeMaker.Assign

用于創(chuàng)建賦值語句語法樹節(jié)點(JCAssign),源碼如下:

public JCAssign Assign(JCExpression lhs, JCExpression rhs) {
        JCAssign tree = new JCAssign(lhs, rhs);
        tree.pos = pos;
        return tree;
    }

參數(shù)解釋:

lhs:賦值語句左邊表達(dá)式

rhs:賦值語句右邊表達(dá)式

TreeMaker.Exec

用于創(chuàng)建可執(zhí)行語句語法樹節(jié)點(JCExpressionStatement),源碼如下:

public JCExpressionStatement Exec(JCExpression expr) {
        JCExpressionStatement tree = new JCExpressionStatement(expr);
        tree.pos = pos;
        return tree;
    }

TreeMaker.Block

用于創(chuàng)建組合語句語法樹節(jié)點(JCBlock),源碼如下:

public JCBlock Block(long flags, List<JCStatement> stats) {
        JCBlock tree = new JCBlock(flags, stats);
        tree.pos = pos;
        return tree;
    }

參數(shù)解釋:

flags:訪問標(biāo)志

stats:語句列表

用法實例:

下面來介紹一下實例來加深對API用法的理解:

1、根據(jù)字符串獲取Name,(利用Names的fromString靜態(tài)方法)

private Name getNameFromString(String s) { return names.fromString(s); }

2、創(chuàng)建變量語句

private JCTree.JCVariableDecl makeVarDef(JCTree.JCModifiers modifiers, String name, JCTree.JCExpression vartype, JCTree.JCExpression init) {
        return treeMaker.VarDef(
                modifiers,
                getNameFromString(name), //名字
                vartype, //類型
                init //初始化語句
        );
    }

3、創(chuàng)建 域/方法 的多級訪問, 方法的標(biāo)識只能是最后一個

例如: java.lang.System.out.println

private JCTree.JCExpression memberAccess(String components) {
    String[] componentArray = components.split("\\.");
    JCTree.JCExpression expr = treeMaker.Ident(getNameFromString(componentArray[0]));
    for (int i = 1; i < componentArray.length; i++) {
        expr = treeMaker.Select(expr, getNameFromString(componentArray[i]));
    }
    return expr;
}

4、聲明變量并賦值(利用以上包裝的方法)

JCTree.JCVariableDecl var = makeVarDef(treeMaker.Modifiers(0), "xiao", memberAccess("java.lang.String"), treeMaker.Literal("methodName"));

生成語句為:String xiao = "methodName";

5、給變量賦值

private JCTree.JCExpressionStatement makeAssignment(JCTree.JCExpression lhs, JCTree.JCExpression rhs) {
    return treeMaker.Exec(
            treeMaker.Assign(
                    lhs,
                    rhs
            )
    );
}
makeAssignment(treeMaker.Ident(getNameFromString("xiao")), treeMaker.Literal("assignment test"));

生成的賦值語句為:xiao = "assignment test";

6、兩個字符串字面量相加并賦值

treeMaker.Exec(
                treeMaker.Assign(treeMaker.Ident(getNameFromString("xiao")),
                        treeMaker.Binary(
                                JCTree.Tag.PLUS,
                                treeMaker.Literal("-Binary operator one"),
                                treeMaker.Literal("-Binary operator two")
                        ))
        );

生成語句為:xiao = "-Binary operator one" + "-Binary operator two";

編譯器會對此語句進(jìn)行優(yōu)化,因為兩個字面量在編譯器即能確定,所以編譯器會把此語句優(yōu)化為:“xiao = "-Binary operator one-Binary operator two";”

7、+=語句

treeMaker.Exec(
                treeMaker.Assignop(
                        JCTree.Tag.PLUS_ASG,
                        treeMaker.Ident(getNameFromString("xiao")),
                        treeMaker.Literal("-Assignop test")
                )
        );

生成語句為:xiao += "-Assignop test";

8、聲明整型變量并賦值

makeVarDef(treeMaker.Modifiers(0), "zhen", memberAccess("java.lang.Integer"), treeMaker.Literal(1));

生成語句為:Integer zhen = 1;

9、++語句

treeMaker.Exec(
                treeMaker.Unary(
                        JCTree.Tag.PREINC,
                        treeMaker.Ident(getNameFromString("zhen"))
                )
        );

生成語句:zhen++;

10、加法語句

treeMaker.Exec(
                treeMaker.Unary(
                        JCTree.Tag.PREINC,
                        treeMaker.Ident(getNameFromString("zhen"))
                )
        );

生成語句:zhen = zhen + 10;

11、方法調(diào)用(以輸出語句舉例)

treeMaker.Exec(
                treeMaker.Assign(
                        treeMaker.Ident(getNameFromString("zhen")),
                        treeMaker.Binary(
                                JCTree.Tag.PLUS,
                                treeMaker.Ident(getNameFromString("zhen")),
                                treeMaker.Literal(10)
                        ))
        );

生成語句:System.out.println(xiao);

12、方法調(diào)用,輸出字符串

JCTree.JCExpressionStatement printVar = treeMaker.Exec(treeMaker.Apply(
                List.of(memberAccess("java.lang.String")),//參數(shù)類型
                memberAccess("java.lang.System.out.println"),
                List.of(treeMaker.Ident(getNameFromString("xiao")))
                )
        );

生成語句:System.out.println("xiao test zhen");

13、if語句

treeMaker.If(
                treeMaker.Binary(
                        JCTree.Tag.LT,
                        treeMaker.Ident(getNameFromString("zhen")),
                        treeMaker.Literal(10)
                ),
                printVar,
                printLiteral
        );

生成語句:

if (zhen < 10) {

           System.out.println(xiao);

} else {

           System.out.println("xiao test zhen");

}

14、if語句(null判斷)

treeMaker.If(
                treeMaker.Parens(
                        treeMaker.Binary(
                                JCTree.Tag.NE,
                                treeMaker.Ident(getNameFromString("xiao")),
                                treeMaker.Literal(TypeTag.BOT, null))
                ),
                printVar,
                printLiteral
        )

生成語句:

if (xiao != null) {

           System.out.println(xiao);

} else {

          System.out.println("xiao test zhen");

}

以上列出了一下常用的語句的語法樹操作方法,希望對理解操作語法樹有幫助。筆者也正在研究中,對編譯原理了解的話,學(xué)起來也就容易多了。

推薦幾本書,看完的話定定會受益匪淺:《編譯原理(高清龍書中文版)》、《兩周自制腳本語言》、《現(xiàn)代編譯器的Java實現(xiàn)(第二版)》。

更多關(guān)于java編譯期修改語法樹的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • ReadWriteLock接口及其實現(xiàn)ReentrantReadWriteLock方法

    ReadWriteLock接口及其實現(xiàn)ReentrantReadWriteLock方法

    下面小編就為大家?guī)硪黄猂eadWriteLock接口及其實現(xiàn)ReentrantReadWriteLock方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • Spring?MVC數(shù)據(jù)響應(yīng)處理詳解

    Spring?MVC數(shù)據(jù)響應(yīng)處理詳解

    這篇文章主要給大家介紹了關(guān)于Spring?MVC數(shù)據(jù)響應(yīng)處理的相關(guān)資料,本教程詳細(xì)的講解SpringMVC框架的使用,非常詳細(xì)的案例講解,一步一步帶你走入springmvc框架的核心,需要的朋友可以參考下
    2022-05-05
  • Java?webservice的POST和GET請求調(diào)用方式

    Java?webservice的POST和GET請求調(diào)用方式

    這篇文章主要介紹了Java?webservice的POST和GET請求調(diào)用方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java視頻斷點上傳的實現(xiàn)示例

    Java視頻斷點上傳的實現(xiàn)示例

    斷點續(xù)傳指的是在下載或上傳時,將下載或上傳任務(wù)人為的劃分為幾個部分,本文主要介紹了Java視頻斷點上傳的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2024-05-05
  • Java中的泛型詳解

    Java中的泛型詳解

    這篇文章主要介紹了Java中的泛型詳解,本文講解了泛型類或接口、從泛型類派生子類、偽泛型、類型通配符、通配符的上限、通配符的下限、擦除和轉(zhuǎn)換等內(nèi)容,需要的朋友可以參考下
    2015-04-04
  • java 中volatile和lock原理分析

    java 中volatile和lock原理分析

    這篇文章主要介紹了java 中volatile和lock原理分析的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • java數(shù)據(jù)庫數(shù)據(jù)分批讀取的實現(xiàn)示例

    java數(shù)據(jù)庫數(shù)據(jù)分批讀取的實現(xiàn)示例

    在處理大量數(shù)據(jù)時,直接從數(shù)據(jù)庫一次性讀取所有數(shù)據(jù)可能會導(dǎo)致內(nèi)存溢出或者性能下降,本文就來介紹一下java數(shù)據(jù)庫數(shù)據(jù)分批讀取的實現(xiàn)示例,感興趣的可以了解一下
    2024-01-01
  • Eclipse中常用快捷鍵匯總

    Eclipse中常用快捷鍵匯總

    這篇文章主要介紹了Eclipse中常用快捷鍵,文中介紹的非常詳細(xì),幫助大家更好的利用eclipse開發(fā),感興趣的朋友可以了解下
    2020-07-07
  • java中注解機(jī)制及其原理的詳解

    java中注解機(jī)制及其原理的詳解

    這篇文章主要介紹了java中注解機(jī)制及其原理的詳解的相關(guān)資料,希望通過本文能幫助到大家,讓大家理解掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-10-10
  • Java實現(xiàn)HTML轉(zhuǎn)為Word的示例代碼

    Java實現(xiàn)HTML轉(zhuǎn)為Word的示例代碼

    本文以Java代碼為例為大家詳細(xì)介紹如何實現(xiàn)將HTML文件轉(zhuǎn)為Word文檔(.docx、.doc)。在實際開發(fā)場景中可參考此方法來轉(zhuǎn)換,感興趣的可以了解一下
    2022-06-06

最新評論