如何获取java运行时动态生成的class文件?
查看运行时生成的文件,以更清楚运行情况。
查看动态生成的类,一般有两个方法:
1. 使用据说是jdk自带包sa-jdi.jar里的工具。
其中,不想自己搞,当然就利用下,sa-jdi.jar 里自带的的sun.jvm.hotspot.tools.jcore.ClassDump就可以把类的class内容dump到文件里。
ClassDump里可以设置两个System properties:
sun.jvm.hotspot.tools.jcore.filter Filter的类名
sun.jvm.hotspot.tools.jcore.outputDir 输出的目录
sa-jdi.jar 里有一个sun.jvm.hotspot.tools.jcore.PackageNameFilter,可以指定Dump哪些包里的类。PackageNameFilter里有一个System property可以指定过滤哪些包:sun.jvm.hotspot.tools.jcore.PackageNameFilter.pkgList。
所以可以通过这样子的命令来使用:
sudo java -classpath "$JAVA_HOME/lib/sa-jdi.jar" -Dsun.jvm.hotspot.tools.jcore.filter=sun.jvm.hotspot.tools.jcore.PackageNameFilter -Dsun.jvm.hotspot.tools.jcore.PackageNameFilter.pkgList=com.test sun.jvm.hotspot.tools.jcore.ClassDump <pid>
不过,我在windows下并没有成功过,原因是还要要求我 start SwDbgSrv.exe,搞不了。
其中sa-jdi.jar文件也不那么好找呢,不过也能找到!
所以,还不如自己动手,丰衣足食!
2. 自己重写一个记录工具,用agent attatch 到进程,然后利用Instrumentation和ClassFileTransformer就可以获取 到类的字节码了。
工具类如下:
package com.xxx.test; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.Arrays; /**
* 动态生成类拦截查看工具
*
* @date 2018/9/15
*/
public class ClazzDumpCustomAgent implements ClassFileTransformer { /**
* 导出过滤表达式,此处为类名前缀, 以 -f 参数指定
*/
private String filterStr; /**
* 导出文件目录根目录, 以 -d 参数指定
*/
private String exportBaseDir = "/tmp/"; /**
* 是否创建多级目录, 以 -r 参数指定
*/
private boolean packageRecursive; public ClazzDumpCustomAgent(String exportBaseDir, String filterStr) {
this(exportBaseDir, filterStr, false);
} public ClazzDumpCustomAgent(String exportBaseDir, String filterStr, boolean packageRecursive) {
if(exportBaseDir != null) {
this.exportBaseDir = exportBaseDir;
}
this.packageRecursive = packageRecursive;
this.filterStr = filterStr;
} /**
* 入口地址
*
* @param agentArgs agent参数
* @param inst
*/
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("agentArgs: " + agentArgs);
String exportDir = null;
String filterStr = null;
boolean recursiveDir = false;
if(agentArgs != null) {
if(agentArgs.contains(";")) {
String[] args = agentArgs.split(";");
for (String param1 : args) {
String[] kv = param1.split("=");
if("-d".equalsIgnoreCase(kv[0])) {
exportDir = kv[1];
}
else if("-f".equalsIgnoreCase(kv[0])) {
filterStr = kv[1];
}
else if("-r".equalsIgnoreCase(kv[0])) {
recursiveDir = true;
}
}
}
else {
filterStr = agentArgs;
}
}
inst.addTransformer(new ClazzDumpCustomAgent(exportDir, filterStr, recursiveDir));
} @Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (needExportClass(className)) {
int lastSeparatorIndex = className.lastIndexOf("/") + 1;
String fileName = className.substring(lastSeparatorIndex) + ".class";
String exportDir = exportBaseDir;
if(packageRecursive) {
exportDir += className.substring(0, lastSeparatorIndex);
}
exportClazzToFile(exportDir, fileName, classfileBuffer); //"D:/server-tool/tmp/bytecode/exported/"
System.out.println(className + " --> EXPORTED");
}
return classfileBuffer;
} /**
* 检测是否需要进行文件导出
*
* @param className class名,如 com.xx.abc.AooMock
* @return y/n
*/
private boolean needExportClass(String className) {
if(filterStr != null) {
if(className.startsWith(filterStr)) {
return true;
}
else {
return false;
}
}
if (!className.startsWith("java") && !className.startsWith("sun")) {
return true;
}
return false;
} /**
* 执行文件导出写入
*
* @param dirPath 导出目录
* @param fileName 导出文件名
* @param data 字节流
*/
private void exportClazzToFile(String dirPath, String fileName, byte[] data) {
try {
File dir = new File(dirPath);
if(!dir.isDirectory()) {
dir.mkdirs();
}
File file = new File(dirPath + fileName);
if (!file.exists()) {
System.out.println(dirPath + fileName + " is not exist, creating...");
file.createNewFile();
}
else { // String os = System.getProperty("os.name"); // 主要针对windows文件不区分大小写问题
// if(os.toLowerCase().startsWith("win")){
// // it's win
// }
try {
int maxLoop = 9999;
int renameSuffixId = 2;
String[] cc = fileName.split("\\.");
do {
Long fileLen = file.length();
byte[] fileContent = new byte[fileLen.intValue()];
FileInputStream in = new FileInputStream(file);
in.read(fileContent);
in.close();
if(!Arrays.equals(fileContent, data)) {
fileName = cc[0] + "_" + renameSuffixId + "." + cc[1];
file = new File(dirPath + fileName);
if (!file.exists()) {
System.out.println("new create file: " + dirPath + fileName);
file.createNewFile();
break;
}
}
else {
break;
}
renameSuffixId++;
maxLoop--;
} while (maxLoop > 0);
}
catch (Exception e) {
System.err.println("exception in read class file..., path: " + dirPath + fileName);
e.printStackTrace();
}
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(data);
fos.close();
}
catch (Exception e) {
System.err.println("exception occur while export class.");
e.printStackTrace();
}
}
}
写好工具类后,将其打包为jar包文件,(如何打包此类请查看上一篇文章: idea中如何将单个java类导出为jar包文件?),假如打包后的文件命名名 clazzdumpcustagent.jar 。
MENIFEST.MF 文件内容如下:
Manifest-Version: 1.0
Premain-Class: com.youge.api.ClazzDumpCustomAgent
使用该jar包工具,进行运行时class文件查看。
在运行项目时,添加javaagent,进行代码导出:
# 在vm参数中,加入该agent
java -javaagent:D:\server-tool\clazzdumpcustagent.jar=-d=D:/server-tool/tmp/bytecode/exported/;-f2=com/alibaba/dubbo;-r xxx
其中:
-d: 设置导出文件的输出目录;
-f: 设置需要导出的字节码的前缀;
-r: 有该参数代表需要进行包目录的创建,否则生成所有文件到一个目录;
然后可以到指定目录下去查看生成的字节码文件了。
最后,使用java反编译工具,查看 java代码就ok了。(可以直接拖进IDE进行解析)
如果不想自己打包,我打了个包放在网上,有需要可自行下载! https://download.csdn.net/download/nihe123yiyang/10670937
使用javaagent, 可以很容易做到无侵入的采集监控数据,做 应用监控必备 啊!
相信在必要的时候,可以派上用场!
如何获取java运行时动态生成的class文件?的更多相关文章
- [转] Java运行时动态生成class的方法
[From] http://www.liaoxuefeng.com/article/0014617596492474eea2227bf04477e83e6d094683e0536000 廖雪峰 / 编 ...
- Java 运行时动态生成class
转载 http://www.liaoxuefeng.com/article/0014617596492474eea2227bf04477e83e6d094683e0536000 Java是一门静态语言 ...
- Java运行时动态加载类之ClassLoader
https://blog.csdn.net/fjssharpsword/article/details/64922083 *************************************** ...
- 使用javassist运行时动态重新加载java类及其他替换选择
在不少的情况下,我们需要对生产中的系统进行问题排查,但是又不能重启应用,java应用不同于数据库的存储过程,至少到目前为止,还不能原生的支持随时进行编译替换,从这种角度来说,数据库比java的动态性要 ...
- java运行时内存模式学习
学习java运行时内存模式: 各区介绍: 方法区(线程共享):用于存放被虚拟机加载的类的元数据:静态变量,常量,以及编译和的代码(字节码),也称为永久代(所有该类的实例被回收,或者此类classLoa ...
- C# 在运行时动态创建类型
C# 在运行时动态的创建类型,这里是通过动态生成C#源代码,然后通过编译器编译成程序集的方式实现动态创建类型 public static Assembly NewAssembly() { //创建编译 ...
- 读书笔记-浅析Java运行时数据区
作为一个 Java 为主语言的程序员,我偶尔也需要 用 C/C++ 写程序,在使用时让我很烦恼的一件事情就是需要对 new 出来的对象进行 delete/free 操作,我老是担心忘了这件事情,从而导 ...
- JVM发展史和Java运行时内存区域
目前三大主流JVM: Sun HotSpot:Sun于1997年收购Longview Technologies公司所得.Sun于2009年被Oracle收购. BEA JRockit:BEA于2002 ...
- 理解JVM之JAVA运行时内存区域
java运行时内存区域划分为方法区,堆区,虚拟机栈区,本地方法栈,程序计数器.其中方法区跟堆区是线程共享的数据区,其他的是线程私有的数据区. 1.程序计数器 程序计数器(PC)是一块较小的内存,他是存 ...
随机推荐
- anaconda安装qt错误
windows下miniconda2安装python3下的qt包报错 conda install qt ERROR conda.core.link:_execute_actions(330): An ...
- 移动端h5调试方法
charles代理 使用技巧如下: http://www.jianshu.com/p/fdd7c681929c 1.手机wifi连接代理 以iphone为例,长按住wifi, 进入下一页,可看到 HT ...
- 每天写两个的java常见面试题—final 和static 的用法
第一次写随笔,可能写的比较乱,更多的是作为自己记忆一些知识的方式.所有记录的东西都是自己的一些理解,很多语言可能还是从其他大牛的博客里面搬过来的. 一.static的作用: static的的作用从三个 ...
- 记一次Struts2 内核问题分析解决
问题场景描述 生产环境某个处理耗时比较长的接口,吞吐能力极差.客服反馈此功能长期处于毫无响应状态. 具体表现 系统启动后第一次调用耗时极慢,长时间不响应.紧随之后发起的请求也同时没有响应. 等待第一次 ...
- 初学html,任务1:一个简单html页面,要求:内容页面装一篇文章 用html来分段
这是主要内容部分,用html实现版块分布. 接下来是样式部分. 让页面所有元素的padding和margin都设置为0 : 否则加入一张大的覆盖的背景图片后,会由于浏览器的缘故,图片周边有白边: 设置 ...
- ubuntu 重启nginx遇到错误
错误如下:Job for nginx.service failed because the control process exited with error code. See "syst ...
- C# BitArray 二进制处理
int k = 6; byte[] bytearr = BitConverter.GetBytes(k); BitArray myBA = new BitArray(bytearr); for (in ...
- NOIP考点
NOIP考点 基础算法 图 树 数论 数据结构 动态规划 搜索 其他算法 省选知识点汇总 图论 数据结构 字符串相关算法及数据结构 数学 计算几何 搜索 动态规划 其他算法 转自:巨佬的博客 加*号是 ...
- 一些你不知道的PHP高级语法汇总
一.执行系统外部命令 system() 输出并返回最后一行shell结果. exec() 不输出结果,返回最后一行shell结果,所有结果可以保存到一个返回的数组里面. passthru() 只调用命 ...
- ffmpeg 推流相关指令
1.rtsp->rtsp(只解封装,不解码) ffmpeg -re -rtsp_transport tcp -i rtsp://usr:passwd@ip:port/h264/ch1/sub/a ...