当前位置: 首页 > news >正文

java 探针两种模式实战

分为两种
程序运行前的agent:premain
程序运行中的agent:agentmain
在程序运行前的agent

javaagent是java命令的一个参数,所以需要通过-javaagent 来指定一个jar包(就是我们要做的代理包)能够实现在主程序运行前来执行我们jar中的方法
1.pom文件中

<plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.5.1</version><!-- 指定maven编译的jdk版本。若不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3 --><configuration><source>8</source><target>8</target></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.2.0</version><configuration><archive><!--自动添加META-INF/MANIFEST.MF --><manifest><addClasspath>true</addClasspath></manifest><manifestEntries><Menifest-Version>1.0</Menifest-Version><!-- <Agent-class>example.AgentMain</Agent-class> --><!-- 必须存在 --><Premain-Class>example.PreMainAgent</Premain-Class><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes><!--                            <Manifest-Version>true</Manifest-Version>--><!--                            <Can-Set-Native-Method-Prefix>true</Can-Set-Native-Method-Prefix>--></manifestEntries></archive></configuration></plugin>
</plugins>

也可以使用META-INF/MANIFEST.MF文件
Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Premain-Class: com.example.PreMainAgent

注意:

  1. 最后需要多一行空行
  2. Can-Redefine-Classes :true表示能重定义此代理所需的类, 默认值为 false(可选)
  3. Can-Retransform-Classes :true 表示能重转换此代理所需的 类,默认值为 false (可选)
  4. Premain-Class :包含 premain 方法的类(类的全路径名)

程序运行前的agent:PreMainAgent 类

方法名称必须为premain
加载顺序为
premain(String agentArgs,Instrumentation inst)
若找不到则执行

premain(String agentArgs)
public class PreMainAgent {public static void premain(String agentArgs,Instrumentation inst) {System.out.println("agent参数:"+agentArgs);}
}

将此服务打成jar包
创建另一个简单的maven项目

public class Application {public static void main(String[] args) {System.out.println("main 运行 ");}
}

启动运行时增加
-javaagent:D:\agent-demo-1.0-SNAPSHOT.jar=option1=value1

运行结果
agent参数:k1=v1
main 运行

在程序运行中的agent:agentmain类

基础的maven项目

public class AgentMain {public static void agentmain(String agentArgs, Instrumentation inst) {inst.addTransformer(new DefineTransformer(),true);System.out.println("----------我是agentmain");System.out.println("----------agentArgs = " + agentArgs);}
}

addTransformer、getAllLoadedClasses,retransformClasses 方法
addTransformer
用于注册Transformer 可以通过编写ClassFileTransformer 接口的实现类来注册我们自己的转换器
类加载的时候会进入我们自己的Transformer中的transformer 函数进行拦截,此处如果不需拦截也可以不处理

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.Scanner;public class DefineTransformer implements ClassFileTransformer {public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {Scanner sc = new Scanner(System.in);System.out.println("Injected Class AgentMainDemo Successfully !");System.out.print("> ");try {InputStream is = Runtime.getRuntime().exec(sc.next()).getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is));String line;StringBuilder sb = new StringBuilder();while ((line = br.readLine()) != null){sb.append(line).append("\n");}System.out.println(sb);} catch (IOException e) {e.printStackTrace();}return classfileBuffer;}
}

getAllLoadedClasses
用于列出所有已加载的class ,可以通过遍历class数组来寻找我们需要重新定义的class

public static void agentmain(String agentArgs, Instrumentation inst) {inst.addTransformer(new DefineTransformer(),true);System.out.println("----------监控探针");Class[] allClass = inst.getAllLoadedClasses();for (Class c : allClass) {System.out.println(c.getName());}
}

retransformClasses
能对已加载的class进行重新定义,如果目标类已经被加载了,可以调用此函数重新触发这个拦截能够达到对已加载的类进行字节码修改的效果。

public static final String ClassName = "org.apache.catalina.core.ApplicationFilterChain";public static void agentmain(String agentArgs, Instrumentation inst) {inst.addTransformer(new DefineTransformer(), true);System.out.println("----------监控探针");Class[] allClass = inst.getAllLoadedClasses();for (Class c : allClass) {if (c.getName().equals(ClassName)) {try {System.out.println("Inject class exit :" + ClassName);//agentmain 是JVM运行时,需要调用 retransformClasses 重定义类 !!inst.retransformClasses(c);} catch (UnmodifiableClassException e) {throw new RuntimeException(e);}}}
}

在pom文件里添加

<plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.5.1</version><!-- 指定maven编译的jdk版本。若不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3 --><configuration><source>8</source><target>8</target></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>3.2.0</version><configuration><archive><!--自动添加META-INF/MANIFEST.MF --><manifest><addClasspath>true</addClasspath></manifest><manifestEntries><Menifest-Version>1.0</Menifest-Version><Agent-class>example.AgentMain</Agent-class><!--                            <Premain-Class>example.PreMainAgent</Premain-Class>--><Can-Redefine-Classes>true</Can-Redefine-Classes><Can-Retransform-Classes>true</Can-Retransform-Classes><!--                            <Manifest-Version>true</Manifest-Version>--><!--                            <Can-Set-Native-Method-Prefix>true</Can-Set-Native-Method-Prefix>--></manifestEntries></archive></configuration></plugin>
</plugins>

打成jar包备用
在另一个项目中添加

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;import java.util.List;public class AttachMain {public static void main(String[] args) throws Exception{// 生成jar包的绝对路径String path = "E:\\test-1.0-SNAPSHOT.jar";// 列出已加载的jvmList<VirtualMachineDescriptor> list = VirtualMachine.list();// 遍历已加载的jvmfor (VirtualMachineDescriptor v:list){// 打印jvm的 displayName 属性System.out.println(v.displayName());// 如果 displayName 为指定的类if (v.displayName().contains("AttachMain")){// 打印pidSystem.out.println("id >>> " + v.id());// 将 jvm 虚拟机的 pid 号传入 attach 来进行远程连接VirtualMachine vm = VirtualMachine.attach(v.id());// 将我们的 agent.jar 发送给虚拟机vm.loadAgent(path);// 解除链接vm.detach();}}}
}

注意,此处tools不会自动加载,需要pom文件里手动加载

<dependency><groupId>com.sun</groupId><artifactId>tools</artifactId><version>1.8</version><scope>system</scope><systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>

运行即可看到

说明已经成功

此时可以启动你的服务,然后修改displayname的判断条件,即可对系统服务是否运行进行监控

后面的服务相当于把上面的jar包挂载到你需要的服务上,可以触发jar包中的代码

http://www.lryc.cn/news/260886.html

相关文章:

  • uniGUI之MASK遮罩
  • DevOps云原生创建devops流水线(微服务项目上传git,打包镜像,部署k8s)
  • 【vim 学习系列文章 13.1 -- 自动命令autocmd 根据文件类型设置vim参数】
  • 算法基础概念之数据结构
  • 解决ES伪慢查询
  • 关于Ubuntu22.04恢复误删文件的记录
  • Docker笔记:Docker Swarm, Consul, Gateway, Microservices 集群部署
  • 浅析AI视频分析与视频管理系统EasyCVR平台及场景应用
  • 跨站点分布式多活存储建设方案概述
  • Github 2023-12-16开源项目日报Top10
  • c++ 中多线程的相关概念与多线程类的使用
  • 深入理解 hash 和 history:网页导航的基础(下)
  • 腾讯文档助力CRM集成:无代码连接电商与广告
  • 学习使用echarts漏斗图的参数配置和应用场景
  • npm ,yarn 更换使用国内镜像源,阿里源,清华大学源
  • vue+react题集整理
  • 线程池ThreadPoolExecutor详解
  • elasticsearch|大数据|kibana的安装(https+密码)
  • vue javascript tree 层级数据处理
  • WPF仿网易云搭建笔记(4):信息流控制之消息订阅
  • 持续集成交付CICD:GitLabCI操作Harbor仓库
  • [C++]——学习模板
  • 大数据技术14:FlinkCDC数据变更捕获
  • SpringDataRedis 基本使用
  • 蓝牙物联网智慧工厂解决方案
  • html的学习笔记
  • 每日一道算法题 8(2023-12-16)
  • Unity项目优化案例二
  • 如何发布自定义 npm 组件包
  • iOS_给View的部分区域截图 snapshot for view