如何获取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)是一块较小的内存,他是存 ...
随机推荐
- makefile入门-初步了解
自己开始学习makefile是由于VScode配置工程文件,看别人的配置不是很懂,于是决定入门学习下makefile. 先来说说makefile是做什么用的:makefile可以实现工程的自动化编译, ...
- 包管理工具-yarn
今天知道了一个新的包管理工具叫yarn,总结如下: 如果你知道npm的使用过程,那么yarn你就觉着相见恨晚呐...... npm存在的问题: >安装的时候无法保证速度的一致性 >安全问题 ...
- POJ 3162.Walking Race 树形dp 树的直径
Walking Race Time Limit: 10000MS Memory Limit: 131072K Total Submissions: 4123 Accepted: 1029 Ca ...
- Fair Candy Swap LT888
Alice and Bob have candy bars of different sizes: A[i] is the size of the i-th bar of candy that Ali ...
- Delphi之Exception获得错误信息
1 unit Unit1; 2 3 interface 4 5 uses 6 Windows, Messages, SysUtils, Variants, Classes, Graphics, Con ...
- git学习笔记:常用命令总结
本文根据廖雪峰的博客,记录下自己的学习笔记.主要记录常用的命令,包括仓库初始化.添加文件.提交修改.新建分支.内容暂存.分支管理.标签管理等内容. git是分布式版本控制系统. 首先是安装,从官网下载 ...
- 【Selenium】【BugList10】smtp发送邮件问题汇总:550/535/554
[场景] 通过126邮箱向QQ邮箱发送HTML格式邮件 [代码1] from email.mime.text import MIMEText from email.header import Head ...
- pyc文件
1.pyc文件 是python预编译后的字节码文件,并不是机器码.2.PyCodeObject 是Python编译器真正编译成的结果: 当python程序运行时,编译的结果是保存在PyCodeObje ...
- 《Linux就该这么学》第十五天课程
本次课所学习的是DNS域名解析服务! 下面提供一些DNS有关的内容 如需进一步学习,请前往https://www.linuxprobe.com/chapter-13.html 工作模式: 1.主服务器 ...
- go语言的条件语句和循环语句
一,条件语句 常见的就是if语句: 单支条件语句: if 条件 :执行语句 (注,如果是没有逻辑运算符连接的话,是可以不需要括号的,也可以加上括号,如:if (条件):执行语句) 双支 ...