java agent

是什么?

在JDK1.5以后,我们可以使用agent技术构建一个独立于应用程序的代理程序(即为Agent),用来协助监测、运行甚至替换其他JVM上的程序。使用它可以实现虚拟机级别的AOP功能。

怎么做:

agent有两种方式

  • jvm 参数形式:调用 premain 方法

  • attach 方式:调用 agentmain 方法

  1. 定义Agent类,为了方便,定义好了两种方式的代码
public class AgentTest {

    /**
     * 以jvm 参数形式启动,运行此方法  -javaagent:xxx.jar
     * @Description 在类加载之前修改字节码
     * @param args
     * @param inst
     */
    public static void premain(String args, Instrumentation inst) {
        System.out.println("agentmain by jvm args");
        inst.addTransformer(new CustomerTransformer(),true);
    }
    /**
     * 动态 attach 方式启动,运行此方法
     * @Description 支持在类加载后再次加载该类
     * @param agentArgs
     * @param inst
     */
    public static void agentmain(String agentArgs, Instrumentation inst) {
        System.out.println("agentmain by process attach");
        inst.addTransformer(new CustomerTransformer(),true);
        // 运行时类定义动态转换需要指定重新定义的类,否则 JVM 无法处理
        try {
            inst.retransformClasses(MyTest.class);
        } catch (UnmodifiableClassException e) {
            e.printStackTrace();
        }
    }
}

2 pom文件新增插件,指定jvm 参数形式运行的Premain-Class 与attach方式运行的Agent-Class

<plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifestEntries>
                            <Premain-Class>com.jxt.AgentTest</Premain-Class>
                            <Agent-Class>com.jxt.AgentTest</Agent-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>

                <executions>
                    <execution>
                        <goals>
                            <goal>attached</goal>
                        </goals>
                        <phase>package</phase>
                    </execution>
                </executions>
            </plugin>
        </plugins>

3 assembly:assembly打包,会生成一个以jar-with-dependencies.jar结尾的jar包

4 定义了对class动态修改的类CustomerTransformer,用来记录方法的执行时间,此代码就是需要自己实现的对业务代码的拦截处理,类似于aop中@before,@After里面的内容:

public class CustomerTransformer implements java.lang.instrument.ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        // 这里我们限制下,只针对目标包下进行耗时统计
        if (!className.startsWith("com/jxt/")) {
            return classfileBuffer;
        }

        CtClass cl = null;
        try {
            ClassPool classPool = ClassPool.getDefault();
            cl = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));

            for (CtMethod method : cl.getDeclaredMethods()) {
                // 所有方法,统计耗时;请注意,需要通过`addLocalVariable`来声明局部变量
                method.addLocalVariable("start", CtClass.longType);
                method.insertBefore("start = System.currentTimeMillis();");
                String methodName = method.getLongName();
                method.insertAfter("System.out.println(\"" + methodName + " cost: \" + (System" +
                        ".currentTimeMillis() - start));");
            }

            byte[] transformed = cl.toBytecode();
            return transformed;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return classfileBuffer;
    }
}

5 编写测试类:

public class MyTest {

    public static void main(String[] args) throws Exception{
        for (;;){
            print();
        }
    }

    public static void print(){
        System.out.println("dfasf");
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

6 运行:

  • jvm参数方式执行:

执行时在运行参数上加上 -javaagent:/Users/***/IdeaProjects/mytest/target/agent_test-1.0-SNAPSHOT-jar-with-dependencies.jar。运行即可,下面是执行结果:

  • attach方式运行:

通过jps查看到已经在运行中的测试类的进程号

编写一个程序,attach到测试类的进程中:如果打包的时候出现"程序包com.sun.tools.attach不存在"请参照https://blog.csdn.net/hosaos/article/details/102930561

public class AttachTest {

    public static void main(String[] args) throws Exception {
        // attach方法参数为目标应用程序的进程号
        VirtualMachine vm = VirtualMachine.attach("72657");
        // 请用你自己的agent绝对地址,替换这个
        vm.loadAgent("/Users/edz/IdeaProjects/mytest/target/agent_test-1.0-SNAPSHOT-jar-with-dependencies.jar");
    }
}

输出如下:

应用场景:

链路追踪skywalking、idea的debug,以及idea破解在vmoption里指定的-agent、以及一些监控工具。

参考文章:https://blog.csdn.net/weixin_45505313/article/details/110739132#3__22

https://blog.csdn.net/weixin_45505313/article/details/110872884

https://mp.weixin.qq.com/s?__biz=MzU3MTAzNTMzMQ==&mid=2247484544&idx=1&sn=d279217a75ca58b0d735fb594d423348

https://blog.csdn.net/liuyueyi25/article/details/104944657?spm=1001.2014.3001.5502

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

每年进步一点点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值