Java字節(jié)碼2-instrument初體驗(yàn)

聲明:原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處。http://www.reibang.com/p/be092b1c72cb

Java字節(jié)碼系列
Java字節(jié)碼1-Agent簡(jiǎn)單上手
Java字節(jié)碼2-instrument初體驗(yàn)
Java字節(jié)碼3-使用ByteBuddy實(shí)現(xiàn)一個(gè)Java-Agent
Java字節(jié)碼4-使用Java-Agent實(shí)現(xiàn)一個(gè)JVM監(jiān)控工具
本系列代碼可見:https://github.com/hawkingfoo/demo-agent

一、概述

在上一節(jié)中Java字節(jié)碼1-Agent簡(jiǎn)單上手中惯裕,我們了解了通過一個(gè)Agent可以在main方法前執(zhí)行。
本節(jié)中,我們將介紹java.lang.instrument祸穷,通過instrument可以實(shí)現(xiàn)一個(gè)Agent來修改類的字節(jié)碼。下面我們會(huì)借助javassist實(shí)現(xiàn)一個(gè)簡(jiǎn)單的性能檢測(cè)工具勺三。目的是檢測(cè)函數(shù)的調(diào)用耗時(shí)雷滚,這里僅僅拋磚引玉,instrument提供的更松耦合的AOP不止于此吗坚。

二祈远、實(shí)現(xiàn)一個(gè)函數(shù)檢測(cè)耗時(shí)Agent

1呆万、修改pom.xml

這里與上一節(jié)不同的是,我們需要額外引入javassist包來增強(qiáng)Agent车份。

<dependency
    <groupId>javassist</groupId
    <artifactId>javassist</artifactId>              
    <version>3.12.1.GA</version>
    <type>jar</type>
 </dependency>

除此之外谋减,我們還需要將此Jar包打包到Agent中,見如下配置:

<plugin> 
  <groupId>org.apache.maven.plugins</groupId>  
  <artifactId>maven-shade-plugin</artifactId>  
  <executions> 
    <execution> 
      <phase>package</phase>  
      <goals> 
        <goal>shade</goal> 
      </goals> 
    </execution> 
  </executions>  
  <configuration> 
    <artifactSet> 
      <includes> 
        <include>javassist:javassist:jar:</include> 
      </includes> 
    </artifactSet> 
  </configuration> 
</plugin>

2扫沼、實(shí)現(xiàn)一個(gè)Agent

public class MyAgent {

    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("this is an perform monitor agent.");
        // 添加 Transformer
        ClassFileTransformer transformer = new PerformMonitorTransformer();
        inst.addTransformer(transformer);
    }
}

與上一節(jié)不同的是出爹,這里添加了一個(gè)Transformer

3缎除、實(shí)現(xiàn)一個(gè)Transformer類

public class PerformMonitorTransformer implements ClassFileTransformer {

    private static final Set<String> classNameSet = new HashSet<>();
    static {
        classNameSet.add("com.example.demo.AgentTest");
    }

    @Override
    public byte[] transform(ClassLoader loader,
                            String className,
                            Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain,
                            byte[] classfileBuffer) throws IllegalClassFormatException {
        try {
            String currentClassName = className.replaceAll("/", ".");
            if (!classNameSet.contains(currentClassName)) { // 僅僅提升Set中含有的類
                return null;
            }
            System.out.println("transform: [" + currentClassName + "]");

            CtClass ctClass = ClassPool.getDefault().get(currentClassName);
            CtBehavior[] methods = ctClass.getDeclaredBehaviors();
            for (CtBehavior method : methods) {
                enhanceMethod(method);
            }
            return ctClass.toBytecode();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private void enhanceMethod(CtBehavior method) throws Exception {
        if (method.isEmpty()) {
            return;
        }
        String methodName = method.getName();
        if (methodName.equalsIgnoreCase("main")) { // 不提升main方法
            return;
        }

        final StringBuilder source = new StringBuilder();
        source.append("{")
                    .append("long start = System.nanoTime();\n") // 前置增強(qiáng): 打入時(shí)間戳
                    .append("$_ = $proceed($$);\n") // 保留原有的代碼處理邏輯
                    .append("System.out.print(\"method:[" + methodName + "]\");").append("\n")
                    .append("System.out.println(\" cost:[\" +(System.nanoTime() -start)+ \"ns]\");") // 后置增強(qiáng)
                .append("}");

        ExprEditor editor = new ExprEditor() {
          @Override
          public void edit(MethodCall methodCall) throws CannotCompileException {
              methodCall.replace(source.toString());
          }
        };
        method.instrument(editor);
    }
}

上面的代碼中以政,我們?cè)鰪?qiáng)了Set中保存的類的方法,這里是“com.example.demo.AgentTest”伴找,如果是其他類需重新設(shè)置或?qū)⒋颂幣渲玫轿募小?/p>

通過enhanceMethod方法盈蛮, 重新寫了其方法的字節(jié)碼,即對(duì)原有的方法體進(jìn)行了環(huán)繞增強(qiáng)技矮。

三抖誉、運(yùn)行

與上一節(jié)中的運(yùn)行方式相同,為了體現(xiàn)本節(jié)的內(nèi)容衰倦,我們修改了Test類增加了兩個(gè)方法:

public class AgentTest {

    private void fun1() {
        System.out.println("this is fun 1.");
    }

    private void fun2() {
        System.out.println("this is fun 2.");
    }

    public static void main(String[] args) {
        AgentTest test = new AgentTest();
        test.fun1();
        test.fun2();
    }

運(yùn)行結(jié)果如下:

this is an perform monitor agent.
transform: [com.example.demo.AgentTest]
this is fun 1.
method:[fun1] cost:[79024ns]
this is fun 2.
method:[fun2] cost:[41164ns]

Process finished with exit code 0

從上面的運(yùn)行結(jié)果可以看到袒炉,我們對(duì)調(diào)用的兩個(gè)方法進(jìn)行了增強(qiáng),得到了其調(diào)用耗時(shí)樊零。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末我磁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子驻襟,更是在濱河造成了極大的恐慌夺艰,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沉衣,死亡現(xiàn)場(chǎng)離奇詭異郁副,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)豌习,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門存谎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人肥隆,你說我怎么就攤上這事既荚。” “怎么了栋艳?”我有些...
    開封第一講書人閱讀 156,936評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵恰聘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)憨琳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,427評(píng)論 1 283
  • 正文 為了忘掉前任旬昭,我火速辦了婚禮篙螟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘问拘。我一直安慰自己遍略,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評(píng)論 6 385
  • 文/花漫 我一把揭開白布骤坐。 她就那樣靜靜地躺著绪杏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纽绍。 梳的紋絲不亂的頭發(fā)上蕾久,一...
    開封第一講書人閱讀 49,785評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音拌夏,去河邊找鬼僧著。 笑死,一個(gè)胖子當(dāng)著我的面吹牛障簿,可吹牛的內(nèi)容都是我干的盹愚。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼站故,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼皆怕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起西篓,我...
    開封第一講書人閱讀 37,696評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤愈腾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后岂津,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顶滩,經(jīng)...
    沈念sama閱讀 44,141評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評(píng)論 2 327
  • 正文 我和宋清朗相戀三年寸爆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了礁鲁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,625評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赁豆,死狀恐怖仅醇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情魔种,我是刑警寧澤析二,帶...
    沈念sama閱讀 34,291評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響叶摄,放射性物質(zhì)發(fā)生泄漏属韧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評(píng)論 3 312
  • 文/蒙蒙 一蛤吓、第九天 我趴在偏房一處隱蔽的房頂上張望宵喂。 院中可真熱鬧,春花似錦会傲、人聲如沸锅棕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽裸燎。三九已至,卻和暖如春泼疑,著一層夾襖步出監(jiān)牢的瞬間德绿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工退渗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脆炎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓氓辣,卻偏偏與公主長(zhǎng)得像秒裕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钞啸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • title: Android AOP之字節(jié)碼插樁author: 陶超description: 實(shí)現(xiàn)數(shù)據(jù)收集SDK時(shí)...
    陶菜菜閱讀 38,127評(píng)論 40 182
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法几蜻,類相關(guān)的語法,內(nèi)部類的語法体斩,繼承相關(guān)的語法梭稚,異常的語法,線程的語...
    子非魚_t_閱讀 31,598評(píng)論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理絮吵,服務(wù)發(fā)現(xiàn)弧烤,斷路器,智...
    卡卡羅2017閱讀 134,629評(píng)論 18 139
  • 今天我6點(diǎn)半就起床了蹬敲,因?yàn)榻裉煲ジ魂柶靼∠景海晕覀兊泌s早點(diǎn),不然就沒水啦伴嗡。( ’ - ’ * ) ...
    竹青奶茶閱讀 733評(píng)論 2 2
  • 主要形態(tài) 圖形形態(tài): 想了解具體技術(shù)細(xì)節(jié)急波,請(qǐng)閱讀<日本蠟燭圖技術(shù)>,丁圣元 譯
    慢牛策略閱讀 2,655評(píng)論 0 1