如何获取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)是一块较小的内存,他是存 ...
随机推荐
- windows socket 文件下载上传
socket 图片 文件 下载上传 数据库 线程池 存入图片 等
- Python之路(第三十三篇) 网络编程:socketserver深度解析
一.socketserver 模块介绍 socketserver是标准库中的一个高级模块,用于网络客户端与服务器的实现.(version = "0.4") 在python2中写作S ...
- Android学习(三)
学号 20189214 <Android程序开发>第八周学习总结 教材学习内容总结 GridView GridView和ListView一样是AbsListView的子类; 都需要一个Ad ...
- Java中的Integer和int
Java中的Integer是引用类型,而int是基本类型.Integer是int的包装器类型. java中的基本类型有布尔类型boolean;字符类型char;整数类型byte,int,long,sh ...
- faster-rcnn 笔记
2019-02-18,15点00 ''' 下面是别人写的原始的笔记,我在上面自己补充了一些. ''' #https://www.cnblogs.com/the-home-of-123/p/974796 ...
- 如何在线更新visual studio installer2017
更新最后100M文件需要开启全局代理才能成功,不然需要等2个小时才能成功!墙太深了..
- node.js 使用 superagent 与 cheerio 完成简单爬虫
目标 建立一个 lesson3 项目,在其中编写代码. 当在浏览器中访问 http://localhost:3000/ 时,输出 CNode(https://cnodejs.org/ ) 社区首页的所 ...
- JSP:getOutputStream() has already been called for this response
JSP页面,用小脚本显示一张图片 <%@page import="java.io.OutputStream"%> <%@page import="jav ...
- 在 ASP.NET Core 中发送邮件遇到的坑_学习笔记
功能需求 因为项目需要有个忘记密码验证邮箱再重新修改密码的功能,然后我选用了很简单的一个方案,通过验证登录用户的邮箱然后发送邮件,通过这个邮件发送的链接地址来最后实现密码修改的小功能. 项目环境及实现 ...
- python3.0 第三天 文件操作
文件操作 对文件操作流程 打开文件,得到文件句柄并赋值给一个变量 通过句柄对文件进行操作 关闭文件 现有文件如下 Somehow, it seems the love I knew was alway ...