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"

maven-jar-plugin

3.0.2

test.PermainAgent

true

true

command: mvn package 


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

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

com.ea.agentloader

ea-agent-loader

1.0.3

在代码里面直接调用自己编写的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;

    }

你可能感兴趣的:(Java agent)