博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何获取java运行时动态生成的class文件?
阅读量:4626 次
发布时间:2019-06-09

本文共 7457 字,大约阅读时间需要 24 分钟。

  查看运行时生成的文件,以更清楚运行情况。

  查看动态生成的类,一般有两个方法:

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 

不过,我在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包文件,(如何打包此类请查看上一篇文章: ),假如打包后的文件命名名 clazzdumpcustagent.jar 。

MENIFEST.MF 文件内容如下:

Manifest-Version: 1.0Premain-Class: com.youge.api.ClazzDumpCustomAgent

使用该jar包工具,进行运行时class文件查看。

在运行项目时,添加javaagent,进行代码导出

# 在vm参数中,加入该agentjava -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进行解析)

如果不想自己打包,我打了个包放在网上,有需要可自行下载! 

 

  使用javaagent, 可以很容易做到无侵入的采集监控数据,做 应用监控必备 啊!  

 

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

转载于:https://www.cnblogs.com/yougewe/p/9651555.html

你可能感兴趣的文章
Python 的property的实现 .
查看>>
搭建Hadoop集群步骤
查看>>
提升jmeter自身性能
查看>>
wordpress调用树形目录
查看>>
数据结构与算法分析(C++版)(第二版)
查看>>
tomcat启动
查看>>
在python3环境安装builtwith模块
查看>>
c语言:婚礼上的谎言
查看>>
[bzoj2333] [SCOI2011]棘手的操作 (可并堆)
查看>>
MVC 4.0 Razor模板引擎 @Html.RenderPartial 与 @Html.RenderAction 区别
查看>>
DHCP服务搭建
查看>>
在路上●我的年轻●勇往直前●匆匆十年
查看>>
LOJ 2721 「NOI2018」屠龙勇士——扩展中国剩余定理
查看>>
hdu3321
查看>>
poj1741(树的点分治)
查看>>
word 生成HTML
查看>>
JSONP--解决ajax跨域问题
查看>>
转:从零开始做app需要做的事情列表
查看>>
转:YUV RGB 常见视频格式解析
查看>>
洛谷P3122 [USACO15FEB]圈住牛Fencing the Herd(计算几何+CDQ分治)
查看>>