美文网首页
Java agent

Java agent

作者: zhangliyun | 来源:发表于2020-01-21 15:31 被阅读0次

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

        }

    相关文章

      网友评论

          本文标题:Java agent

          本文链接:https://www.haomeiwen.com/subject/fzvpvctx.html