How to obtain the class file dynamically generated by java runtime

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:

Back to top

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!

Back to top

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, 可以很容易做到无侵入的采集监控数据,做 应用监控必备 啊!  

相信在必要的时候,可以派上用场!

Guess you like

Origin blog.csdn.net/heikeb/article/details/128946056