How to get the class file dynamically generated by java runtime?
Table of contents
chatgpt free experience: http://www.chat136.com
chatgpt learning: http://me.chat136.com
Look at the files generated at runtime to get a better idea of what's going on.
There are generally two ways to view dynamically generated classes:
1. Use the tool that is said to be in the package sa-jdi.jar that comes with jdk.
Among them, if you don’t want to do it yourself, of course, you can use the sun.jvm.hotspot.tools.jcore.ClassDump that comes with sa-jdi.jar to dump the class content of the class into the file.
Two System properties can be set in ClassDump:
sun.jvm.hotspot.tools.jcore.filter Filter class name
The output directory of sun.jvm.hotspot.tools.jcore.outputDir
There is a sun.jvm.hotspot.tools.jcore.PackageNameFilter in sa-jdi.jar, which can specify the classes in which packages to Dump. There is a System property in PackageNameFilter to specify which packages to filter: sun.jvm.hotspot.tools.jcore.PackageNameFilter.pkgList.
So it can be used with a command like this:
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>
However, I have not succeeded under Windows, because I am required to start SwDbgSrv.exe, which is impossible.
Among them, the sa-jdi.jar file is not so easy to find, but it can be found!
Therefore, it is better to do it yourself and have enough food and clothing!
2. Rewrite a recording tool by yourself, use agent attach to the process, and then use Instrumentation and ClassFileTransformer to obtain the bytecode of the class.
The tool classes are as follows:
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;
/**
* Dynamically generated class interception viewing tool
*
* @date 2018/9/15
*/publicclass ClazzDumpCustomAgent implements ClassFileTransformer {
/**
* Export filter expression, here is the class name prefix, specified by the -f parameter
*/private String filterStr;
/**
* The root directory of the export file directory, specified with the -d parameter
*/private String exportBaseDir = "/tmp/";
/**
* Whether to create a multi-level directory, specified by the -r parameter
*/privateboolean 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;
}
/**
* Entry address
*
* @param agentArgs agent parameters
* @param inst
*/publicstaticvoid 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 ];
}
elseif("-f".equalsIgnoreCase(kv[0])) {
filterStr = kv[1];
}
elseif("-r".equalsIgnoreCase(kv[0])) {
recursiveDir = true;
}
}
}
else {
filterStr = agentArgs;
}
}
inst.addTransformer(new ClazzDumpCustomAgent(exportDir, filterStr, recursiveDir));
}
@Override
publicbyte[] 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;
}
/**
* Detect if file export is required
*
* @param className class name, such as com.xx.abc.AooMock
* @return y/n
*/privateboolean needExportClass(String className) {
if(filterStr != null) {
if(className.startsWith(filterStr)) {
returntrue;
}
else {
returnfalse;
}
}
if (!className.startsWith("java") && !className.startsWith("sun")) {
returntrue;
}
returnfalse;
}
/**
* 执行文件导出写入
*
* @param dirPath 导出目录
* @param fileName 导出文件名
* @param data 字节流
*/privatevoid 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 = newbyte[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, 可以很容易做到无侵入的采集监控数据,做 应用监控必备 啊!
相信在必要的时候,可以派上用场!