Java agent

2020-01-21  本文已影响0人  zhangliyun

什么是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;

    }

上一篇 下一篇

猜你喜欢

热点阅读