简单Java agent实现

简介

一个简单的Java agent实现。本质是一个jar包,必须依附一个Java应用程序进行。
原文:https://knife.blog.csdn.net/article/details/121794917

使用方法

在一个普通 Java 程序(带有 main 函数的 Java 类)运行时,通过 -javaagent 参数指定一个特定的 jar 文件(包含 Instrumentation 代理)来启动 Instrumentation 的代理程序。

静态agent

在main加载之前运行agent。

静态Instrument(启动时)加载Instrument过程:
创建并初始化 JPLISAgent;
监听VMInit事件,在JVM初始化完成之后做下面的事情:
创建InstrumentationImpl对象;
监听ClassFileLoadHook事件;
调用InstrumentationImpl的loadClassAndCallPremain方法,在这个方法里会去调用javaagent中MANIFEST.MF里指定的Premain-Class类的premain方法 ;
解析javaagent中MANIFEST.MF文件的参数,并根据这些参数来设置JPLISAgent里的一些内容。

动态agent

在main运行之后运行agent。

动态Instrument运行时加载Instrument过程:
通过JVM的attach机制来请求目标JVM加载对应的agent,过程大致如下:
创建并初始化JPLISAgent;
解析 javaagent 里 MANIFEST.MF 里的参数;
创建 InstrumentationImpl 对象;
监听 ClassFileLoadHook 事件;
调用 InstrumentationImpl 的loadClassAndCallAgentmain方法,在这个方法里会去调用javaagent里 MANIFEST.MF 里指定的Agent-Class类的agentmain方法。

简单示例

agent程序

项目结构
项目demo-java-agent
1.premain方法
在src目录下创建软件包com.example.a下新建一个java类。

package com.example.a;

import java.lang.instrument.Instrumentation;

public class DemoAgent {
    /**
     * 该方法在main方法之前运行,与main方法运行在同一个JVM中
     */
    public static void premain(String arg, Instrumentation instrumentation) {
        System.out.println("agent的premain(String arg, Instrumentation instrumentation)方法");
    }

    /**
     * 若不存在 premain(String agentArgs, Instrumentation inst),
     * 则会执行 premain(String agentArgs)
     */
    public static void premain(String arg) {
        System.out.println("agent的premain(String arg)方法");
    }
}

2.提供META-INF/MANIFEST.MF
在src的目录下新建META-INF文件夹,在里边新建MANIFEST.MF文件(注意最后一行必须是空行)。

Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Premain-Class: com.example.a.DemoAgent

Premain-Class :包含 premain 方法的类(类的全路径名)
Agent-Class :包含 agentmain 方法的类(类的全路径名)
Boot-Class-Path :设置引导类加载器搜索的路径列表。查找类的特定于平台的机制失败后,引导类加载器会搜索这些路径。按列出的顺序搜索路径。列表中的路径由一个或多个空格分开。路径使用分层 URI 的路径组件语法。如果该路径以斜杠字符(“/”)开头,则为绝对路径,否则为相对路径。相对路径根据代理 JAR 文件的绝对路径解析。忽略格式不正确的路径和不存在的路径。如果代理是在 VM 启动之后某一时刻启动的,则忽略不表示 JAR 文件的路径。(可选)
Can-Redefine-Classes :true表示能重定义此代理所需的类,默认值为 false(可选)
Can-Retransform-Classes :true 表示能重转换此代理所需的类,默认值为 false (可选)
Can-Set-Native-Method-Prefix: true表示能设置此代理所需的本机方法前缀,默认值为 false(可选)

3.将其打包为jar包
步骤1:打包路径
文件=》项目结构=》工件=》添加=》JAR=》来自具有依赖项的模块
在这里插入图片描述
在这里插入图片描述

步骤2:打包配置
在这里插入图片描述
步骤3:打包
构建=》构建工件=》构建
在这里插入图片描述
在这里插入图片描述
此时项目结构为
在这里插入图片描述

项目程序

项目结构
在这里插入图片描述

1.main程序
在src目录下创建软件包com.example.a 新建java类

package com.example.a;

public class Demo {
    public static void main(String[] args) {
        System.out.println("应用的main方法");
    }
}

2.将其打包为jar包(打包步骤和agent略有不同)
步骤1:打包路径
文件=》项目结构=》工件=》添加=》JAR=》来自具有依赖项的模块

在这里插入图片描述
步骤2:打包配置
在这里插入图片描述
另外要注意:Directory for META-INF/MANIFEST.MF这里,会自动生成出来。如果没有自动生成或者是灰色的,重新操作上述步骤。否则,后边用java -jar来执行时会失败,报错为:
Error: Invalid or corrupt jarfile jar

执行完上一步之后,左侧会生成META-INF文件夹。
先点击“Apply”,再点击“OK”。
在这里插入图片描述
步骤3:打包
构建=》构建工件=》构建
如上

测试

java -javaagent:E:\rasp\demo-java-agent.jar -jar demo-java.jar

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java Agent是一种Java技术,它可以在程序运行时动态地修改或增强现有的Java类。Java Agent可以用于很多场景,其中之一就是实现Java应用程序的热更新。下面是一个简单Java Agent热更新的示例: 1.编写Java Agent程序,实现热更新功能。具体实现可以使用Java工具包tools.jar提供的agentmain方法,该方法可以在程序运行时动态地修改或增强现有的Java类。以下是一个简单Java Agent程序示例: ```java public class HotUpdateAgent { public static void agentmain(String agentArgs, Instrumentation inst) throws Exception { // 获取要更新的类的类名 String className = "com.example.demo.HelloWorld"; // 获取要更新的类的字节码文件 byte[] classBytes = getClassBytes(className); // 重新定义要更新的类 ClassDefinition classDefinition = new ClassDefinition(Class.forName(className), classBytes); inst.redefineClasses(classDefinition); } private static byte[] getClassBytes(String className) throws Exception { // 读取要更新的类的字节码文件 FileInputStream fis = new FileInputStream(className.replaceAll("\\.", "/") + ".class"); ByteArrayOutputStream bos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { bos.write(buffer, 0, len); } fis.close(); bos.close(); return bos.toByteArray(); } } ``` 2.在需要热更新的Java应用程序中,使用Java Agent实现热更新。具体实现可以使用Java命令的-javaagent参数来指定Java Agent程序,并使用HotUpdateServer类来启动Java应用程序。以下是一个简单Java应用程序示例: ```java public class HelloWorld { public static void main(String[] args) throws Exception { while (true) { System.out.println("Hello World!"); Thread.sleep(1000); } } } ``` 3.使用HotUpdateServer类来启动Java应用程序,并使用Java命令的-javaagent参数来指定Java Agent程序。以下是一个简单的HotUpdateServer类示例: ```java public class HotUpdateServer { public static void main(String[] args) throws Exception { // 获取要更新的Java应用程序的pid String pid = args[0]; // 获取要更新的类的项目目录和类名 String classPath = args[1]; // 获取执行引擎jar的路径 String enginePath = args[2]; // 启动Java应用程序 Process process = Runtime.getRuntime().exec("java -javaagent:" + enginePath + " -cp " + classPath + " HelloWorld"); // 获取Java应用程序的输入流和输出流 InputStream inputStream = process.getInputStream(); OutputStream outputStream = process.getOutputStream(); // 启动热更新线程 new Thread(() -> { while (true) { try { // 等待用户输入命令 Scanner scanner = new Scanner(System.in); String command = scanner.nextLine(); // 发送命令到Java应用程序 outputStream.write((command + "\n").getBytes()); outputStream.flush(); // 读取Java应用程序的输出 byte[] buffer = new byte[1024]; int len; while ((len = inputStream.read(buffer)) != -1) { System.out.write(buffer, 0, len); } } catch (Exception e) { e.printStackTrace(); } } }).start(); // 等待Java应用程序退出 process.waitFor(); } } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值