如何获取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)是一块较小的内存,他是存 ...
随机推荐
- c#简单的数据库查询与绑定DataGridView。
1配置文件 (两种写法) <connectionStrings> <add name="connStr" connectionString="se ...
- vue.js中axios的封装
基于前文所述,axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,它有很多优秀的特性,例如拦截请求和响应.取消请求.转换json.客户端防御XSRF等. 如果还对a ...
- Maven 的这 7 个问题你思考过没有?
在如今的互联网项目开发当中,特别是Java领域,可以说Maven随处可见.Maven的仓库管理.依赖管理.继承和聚合等特性为项目的构建提供了一整套完善的解决方案,可以说如果你搞不懂Maven,那么一个 ...
- Chapter5_初始化与清理_this关键字
this关键字是Java中一类很特殊的关键字,首先它只能在方法内使用,用来表示调用这个方法的对象,在这一点上this和其他对对象的引用的操作是相同的.我们之所以可以在方法内部访问到它是因为编译器在方法 ...
- 55行代码实现Java线程死锁
死锁是Java多线程的重要概念之一,也经常出现在各大公司的笔试面试之中.那么如何创造出一个简单的死锁情况?请看代码: class Test implements Runnable { boolean ...
- 虹软人脸检测和识别C# - API
using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D ...
- 第四次Scrum编码冲刺!!!!
一.总体任务: 本次冲刺是完成对图书馆管理系统的最后三个功能的实现------管理员对用户授权.用户注销和用户查询 二.个人任务及完成情况: 本人本次的任务是实现对管理员对用户授权部分的界面与部 ...
- [少数派]如何学习Git
用玩游戏的方式学习 Git 目录 为什么要学习 Git 怎么学习 Git Learn Git Branching 其他学习资源 用游戏的方式来学习,是一种有趣而高效的方式. 从刚接触电脑时的打字练习软 ...
- final,finally,finalize
final:可以修饰属性,可以修饰方法(方法不能被重写,可以继承),可以修饰类(该类不能被继承,不能产生子类) finally:无论什么情况,都会执行 finalize:垃圾回收时,调用此方法
- 突破内网限制上网(ssh+polipo)
最近到客户这里来做项目,发现客户对网络的把控实在严格,很多网站都不能访问到,搜索到的技术文档也屏蔽了.突然想到了FQ工具的原理,刚好自己也有台服务器在外头,部署个Polipo代理然后用ssh隧道连接. ...