java agent使用

什么是java agent

本质是一个jar包中的类,有两种实现,第一种是通过permain()函数实现。这种javaagent会在宿主程序的main函数的启动前启动自己premain函数,这时候会得到一个Instrumentation对象,我们可以通过Instrumentation对象对还未加载的class进行拦截与修改。

还有一种实现方式是利用agentmain()函数。VirtualMachine类的attach(pid)方法可以将当前进程attach到一个运行中的java进程上,接着利用loadAgent(agentJarPath)来将含符合格式且含有agentmain函数的jar包注入到对应的进程,调用loadAgent函数后,对应的进程中会多出一个Instrumentation对象,这个对象会被当作agentmain的一个参数。对应进程接着会调用agentmain函数,进而操作Instrumentation对象,Instrumentation对象可以在class加载前拦截字节码进行修改,也可以对已经加载的class重新让它加载,并拦截且修改其中的内容,跟进程注入差不多,具体做什么操作,取决于我们的jar文件中的agentmain函数怎么写。

Java agent的使用方式有两种:

实现premain方法,在JVM启动前加载。

实现agentmain方法,在JVM启动后加载。

agent基础使用

环境搭建

agent项目源码

agent:

package com.naihe;

import java.io.IOException;
import java.lang.instrument.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.ProtectionDomain;

public class agent {
    //java agent 入口
    public static void premain(String agentOps, Instrumentation inst) {
        System.out.println("=========premain方法执行========");
        simpleDemo(agentOps, inst);
    }
    public static void agentmain(String agentOps, Instrumentation inst) {
        System.out.println("=========agentmain方法执行========");
        simpleDemo(agentOps, inst);
        //transform是会对尚未加载的类进行增加代理层,这里是已经运行中的jvm,所以类以及被加载了
        //必须主动调用retransformClasses让jvm再对运行中的类进行加上代理层
        for (Class allLoadedClass : inst.getAllLoadedClasses()) {
            //这里的Test路径,修改成你自己机器agent-demo-web工程的Test类的路径
            if(allLoadedClass.getName().contains("com.naihe.Demo")){
                try {
                    inst.retransformClasses(allLoadedClass);
                } catch (UnmodifiableClassException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void simpleDemo(String agentOps, Instrumentation inst) {
        inst.addTransformer(new ClassFileTransformer() {
            @Override
            public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
                //判断是指定的class
                if ("com/naihe/Demo".equals(className)) {
                    try {
                        //获取更改后的类class 字节数组
                        String path="C:/Users/12107/Desktop/Demo2.class";
                        classfileBuffer = Files.readAllBytes(Paths.get(path));
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                return classfileBuffer;
            }
        },true);
    }

}

app项目源码

application:

Demo:

attach:

package com.naihe;

import com.sun.tools.attach.*;

import java.io.IOException;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;

public class attach {
    public static void main(String[] args) {
        //查找所有jvm进程,排除attach测试工程
        List<VirtualMachineDescriptor> attach = VirtualMachine.list()
                .stream()
                .filter(jvm -> {
                    return !jvm.displayName().contains("attach");
                }).collect(Collectors.toList());
        for (int i = 0; i < attach.size(); i++) {
            System.out.println("[" + i + "] " + attach.get(i).displayName()+":"+attach.get(i).id());
        }
        System.out.println("请输入需要attach的pid编号");
        Scanner scanner = new Scanner(System.in);
        String s = scanner.nextLine();
        VirtualMachineDescriptor virtualMachineDescriptor = attach.get(new Integer(s));
        try {
            VirtualMachine virtualMachine = VirtualMachine.attach(virtualMachineDescriptor.id());
            virtualMachine.loadAgent("C:\\Users\\12107\\Desktop\\agent.jar", "param");
            virtualMachine.detach();
        } catch (AttachNotSupportedException e) {
            System.out.println("AttachNotSupportedException:" + e.getMessage());
        } catch (IOException e) {
            System.out.println("IOException:" + e.getMessage());
        } catch (AgentLoadException e) {
            System.out.println("AgentLoadException:" + e.getMessage());
        } catch (AgentInitializationException e) {
            System.out.println("AgentInitializationException:" + e.getMessage());
        }
    }
}

retransformClasses:对于已经加载的类重新进行转换处理,即会触发重新加载类定义,需要注意的是,新加载的类不能修改旧有的类声明,譬如不能增加属性、不能修改方法声明

静态修改class

一,将agent打包成jar包

进入classes目录

这边直接打包得com文件,因为这只是一个小Demo给大家演示一下,并没有使用字节码相关的库

二,修改MANIFEST.MF

使用解压工具打开MANIFEST.MF,并修改内容

三,加载agent.jar

运行app中的application

使用ideal加载jar

动态修改class

清除之前的内容

正常运行

运行attach

可以看到Demo的test方法已经被修改了

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值