Java agent

什么是agent?agent 能做什么

https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package-summary.html

An agent is deployed as a JAR file. An attribute in the JAR file manifest specifies the agent class which will be loaded to start the agent. For implementations that support a command-line interface, an agent is started by specifying an option on the command-line. Implementations may also support a mechanism to start agents some time after the VM has started. For example, an implementation may provide a mechanism that allows a tool to attach to a running application, and initiate the loading of the tool's agent into the running application. The details as to how the load is initiated, is implementation dependent.    

agent作为一个独立的jar ,加在classpath 上 -javaagent:jarpath[=options] ,在JVM启动后会调用agent 里的premain函数。它的作用提供了一种机制用代码的方式让agent attach到running的程序,修改字节码运行,注意这里的修改是添加代码,所以它不会改变代码原来的执行结果。

如何启动一个agent,有两个方法:

方法一:按照如下方法修改pom.xml 执行mvn package 编译出agent的jar,启动时加"-javaagent:target/premain-agent-1.0-SNAPSHOT.jar"

<plugin>

<artifactId>maven-jar-plugin</artifactId>

<version>3.0.2</version>

<configuration>

<archive>

<manifestEntries>

<Premain-Class>test.PermainAgent</Premain-Class>

<Can-Redefine-Classes>true</Can-Redefine-Classes>

<Can-Retransform-Classes>true</Can-Retransform-Classes>

</manifestEntries>

</archive>

</configuration>

</plugin>

command: mvn package 


 在intelliJ 启动时,加-javaagent:xxx.jar

方法二: 使用 ea-agent-loader,这样可以省去打包的步骤,直接在代码里面用agent ,当然需要在pom.xml 里面加入ea-agent-loader的依赖

<dependency>

<groupId>com.ea.agentloader</groupId>

<artifactId>ea-agent-loader</artifactId>

<version>1.0.3</version>

</dependency>

在代码里面直接调用自己编写的Agent class  PermainAgent

public static void main1( String[] args ) {

        AgentLoader.loadAgentClass(PermainAgent.class.getName(), null);

        ATM atm = new ATM();

        atm.hi();

}

一个示例显示了如何编写一个MyInstrumentationAgent ,通过修改指定class ATM 的 withdrawMoney 函数,在不修改源码的情况下,统计该函数的运行时间并打印出。

没有agent ,调用ATM.withdrawMoney 不会打印出运行所花时间



MyInstrumentationAgent ,调用ATM.withdrawMoney 会打印出运行所花时间

具体的代码见https://github.com/kellyzly/javaagent.

运行结果:

20-01-21 06:10:07:788 INFO main test.ATM:14 - [Application] Withdrawal operation completed in:0 seconds!


核心代码的解释:ATM#withdrawMoney调用前, 当JVM加载了ATM这个class, MyInstrumentationAgent截获了这个class,调用ATMTransformer#transform 修改了ATM#withdrawMoney方法,在方法的字节码加入以下这段

LOGGER.info(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!

ATMTransformer#transform

@Override

    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,

            ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

        byte[] byteCode = classfileBuffer;

        String finalTargetClassName = this.targetClassName.replaceAll("\\.", "/"); //replace . with /

        if (!className.equals(finalTargetClassName)) {

            return byteCode;

        }

        if (className.equals(finalTargetClassName) && loader.equals(targetClassLoader)) {

            LOGGER.info("[Agent] Transforming class MyAtm");

            try {

                ClassPool cp = ClassPool.getDefault();

                CtClass cc = cp.get(targetClassName);

                CtMethod m = cc.getDeclaredMethod(WITHDRAW_MONEY_METHOD);

                m.addLocalVariable("startTime", CtClass.longType);

                m.insertBefore("startTime = System.currentTimeMillis();");

                StringBuilder endBlock = new StringBuilder();

                m.addLocalVariable("endTime", CtClass.longType);

                m.addLocalVariable("opTime", CtClass.longType);

                endBlock.append("endTime = System.currentTimeMillis();");

                endBlock.append("opTime = (endTime-startTime)/1000;");

                endBlock.append("LOGGER.info(\"[Application] Withdrawal operation completed in:\" + opTime + \" seconds!\");");

                m.insertAfter(endBlock.toString());

                byteCode = cc.toBytecode();

                cc.detach();

            } catch (NotFoundException | CannotCompileException | IOException e) {

                System.out.println("Exception"+ e);

            }

        }

        return byteCode;

    }

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 考虑下这个问题:怎么知道我写的方法执行了多久? 再考虑下:怎么知道所有方法分别执行了多久? 初学者都会的方法 上面...
    KobeLee阅读 9,949评论 0 1
  • 目前我们的业务遇到线上问题时经常需要加log调试。从加一行log到push代码再到jekens编译、打包、最后再部...
    何轩hexuan阅读 4,924评论 0 1
  • 下载skywalking探针发布版本 前向发布页面 部署探针 拷贝skywalking-agent目录到所需位置,...
    yshenhn阅读 9,074评论 3 0
  • 参考资料 instrument 规范 https://docs.oracle.com/javase/8/docs/...
    王某某的笔记阅读 9,611评论 0 5
  • 晚上看了部分招聘信息。还未投简历。 今天请客吃饭,洗了一大盆菜。 早餐午餐都没吃,只吃了晚餐。 尹约我去吃她同学的...
    WANGRUIXUE阅读 1,631评论 0 0