Java:从 JAR 文件中的类文件中获取方法存根的简单方法?反射?

发布于 2024-08-29 15:55:19 字数 171 浏览 3 评论 0原文

我正在寻找一种方法来获取 jar 文件中所有类的方法存根列表。 我不知道从哪里开始...我可以使用 Reflection 或 Javassist 或其他一些我还没听说过的工具吗? 至少可以解压 jar、反编译类文件并使用行解析器扫描方法,但我认为这是最肮脏的方法;-)

有什么想法吗?

亲切的问候

I'm searching for a way to get a list of method stubs of all classes within a jar file.
I'm not sure where to start... May I use Reflection or Javassist or some other tools of which I've not heard yet!?
At least it may be possible to unpack the jar, decompile the class files and scan with a line parser for methods, but I think that is the most dirtiest way ;-)

Any ideas?

Kind Regards

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

静谧 2024-09-05 15:55:19

构建于 aioobe 的回答,您还可以使用 ASM 的树 API(而不是其访问者 API)来解析 JAR 文件中包含的类文件的内容。此外,您还可以使用 JarFile 类。以下是如何完成此操作的示例:

printMethodStubs 方法接受 JarFile 并继续打印所有类文件中包含的所有方法的描述。

public void printMethodStubs(JarFile jarFile) throws Exception {
    Enumeration<JarEntry> entries = jarFile.entries();
    while (entries.hasMoreElements()) {
        JarEntry entry = entries.nextElement();

        String entryName = entry.getName();
        if (entryName.endsWith(".class")) {
            ClassNode classNode = new ClassNode();

            InputStream classFileInputStream = jarFile.getInputStream(entry);
            try {
                ClassReader classReader = new ClassReader(classFileInputStream);
                classReader.accept(classNode, 0);
            } finally {
                classFileInputStream.close();
            }

            System.out.println(describeClass(classNode));
        }
    }
}

describeClass 方法接受 ClassNode 对象并继续描述它及其关联的方法:

public String describeClass(ClassNode classNode) {
    StringBuilder classDescription = new StringBuilder();

    Type classType = Type.getObjectType(classNode.name);



    // The class signature (e.g. - "public class Foo")
    if ((classNode.access & Opcodes.ACC_PUBLIC) != 0) {
        classDescription.append("public ");
    }

    if ((classNode.access & Opcodes.ACC_PRIVATE) != 0) {
        classDescription.append("private ");
    }

    if ((classNode.access & Opcodes.ACC_PROTECTED) != 0) {
        classDescription.append("protected ");
    }

    if ((classNode.access & Opcodes.ACC_ABSTRACT) != 0) {
        classDescription.append("abstract ");
    }

    if ((classNode.access & Opcodes.ACC_INTERFACE) != 0) {
        classDescription.append("interface ");
    } else {
        classDescription.append("class ");
    }

    classDescription.append(classType.getClassName()).append("\n");
    classDescription.append("{\n");



    // The method signatures (e.g. - "public static void main(String[]) throws Exception")
    @SuppressWarnings("unchecked")
    List<MethodNode> methodNodes = classNode.methods;

    for (MethodNode methodNode : methodNodes) {
        String methodDescription = describeMethod(methodNode);
        classDescription.append("\t").append(methodDescription).append("\n");
    }



    classDescription.append("}\n");

    return classDescription.toString();
}

describeMethod 方法接受 MethodNode 并返回描述方法签名的字符串:

public String describeMethod(MethodNode methodNode) {
    StringBuilder methodDescription = new StringBuilder();

    Type returnType = Type.getReturnType(methodNode.desc);
    Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc);

    @SuppressWarnings("unchecked")
    List<String> thrownInternalClassNames = methodNode.exceptions;

    if ((methodNode.access & Opcodes.ACC_PUBLIC) != 0) {
        methodDescription.append("public ");
    }

    if ((methodNode.access & Opcodes.ACC_PRIVATE) != 0) {
        methodDescription.append("private ");
    }

    if ((methodNode.access & Opcodes.ACC_PROTECTED) != 0) {
        methodDescription.append("protected ");
    }

    if ((methodNode.access & Opcodes.ACC_STATIC) != 0) {
        methodDescription.append("static ");
    }

    if ((methodNode.access & Opcodes.ACC_ABSTRACT) != 0) {
        methodDescription.append("abstract ");
    }

    if ((methodNode.access & Opcodes.ACC_SYNCHRONIZED) != 0) {
        methodDescription.append("synchronized ");
    }

    methodDescription.append(returnType.getClassName());
    methodDescription.append(" ");
    methodDescription.append(methodNode.name);

    methodDescription.append("(");
    for (int i = 0; i < argumentTypes.length; i++) {
        Type argumentType = argumentTypes[i];
        if (i > 0) {
            methodDescription.append(", ");
        }
        methodDescription.append(argumentType.getClassName());
    }
    methodDescription.append(")");

    if (!thrownInternalClassNames.isEmpty()) {
        methodDescription.append(" throws ");
        int i = 0;
        for (String thrownInternalClassName : thrownInternalClassNames) {
            if (i > 0) {
                methodDescription.append(", ");
            }
            methodDescription.append(Type.getObjectType(thrownInternalClassName).getClassName());
            i++;
        }
    }

    return methodDescription.toString();
}

Building upon aioobe's answer, you can also use ASM's tree API (as opposed to its visitor API) to parse the contents of the class files contained within your JAR file. As well, you can read the files contained within the JAR file using the JarFile class. Here is an example of how this could be done:

The printMethodStubs method accepts a JarFile and proceeds to print out descriptions of all methods contained within all class files.

public void printMethodStubs(JarFile jarFile) throws Exception {
    Enumeration<JarEntry> entries = jarFile.entries();
    while (entries.hasMoreElements()) {
        JarEntry entry = entries.nextElement();

        String entryName = entry.getName();
        if (entryName.endsWith(".class")) {
            ClassNode classNode = new ClassNode();

            InputStream classFileInputStream = jarFile.getInputStream(entry);
            try {
                ClassReader classReader = new ClassReader(classFileInputStream);
                classReader.accept(classNode, 0);
            } finally {
                classFileInputStream.close();
            }

            System.out.println(describeClass(classNode));
        }
    }
}

The describeClass method accepts a ClassNode object and proceeds to describe it and its associated methods:

public String describeClass(ClassNode classNode) {
    StringBuilder classDescription = new StringBuilder();

    Type classType = Type.getObjectType(classNode.name);



    // The class signature (e.g. - "public class Foo")
    if ((classNode.access & Opcodes.ACC_PUBLIC) != 0) {
        classDescription.append("public ");
    }

    if ((classNode.access & Opcodes.ACC_PRIVATE) != 0) {
        classDescription.append("private ");
    }

    if ((classNode.access & Opcodes.ACC_PROTECTED) != 0) {
        classDescription.append("protected ");
    }

    if ((classNode.access & Opcodes.ACC_ABSTRACT) != 0) {
        classDescription.append("abstract ");
    }

    if ((classNode.access & Opcodes.ACC_INTERFACE) != 0) {
        classDescription.append("interface ");
    } else {
        classDescription.append("class ");
    }

    classDescription.append(classType.getClassName()).append("\n");
    classDescription.append("{\n");



    // The method signatures (e.g. - "public static void main(String[]) throws Exception")
    @SuppressWarnings("unchecked")
    List<MethodNode> methodNodes = classNode.methods;

    for (MethodNode methodNode : methodNodes) {
        String methodDescription = describeMethod(methodNode);
        classDescription.append("\t").append(methodDescription).append("\n");
    }



    classDescription.append("}\n");

    return classDescription.toString();
}

The describeMethod method accepts a MethodNode and returns a String describing the method's signature:

public String describeMethod(MethodNode methodNode) {
    StringBuilder methodDescription = new StringBuilder();

    Type returnType = Type.getReturnType(methodNode.desc);
    Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc);

    @SuppressWarnings("unchecked")
    List<String> thrownInternalClassNames = methodNode.exceptions;

    if ((methodNode.access & Opcodes.ACC_PUBLIC) != 0) {
        methodDescription.append("public ");
    }

    if ((methodNode.access & Opcodes.ACC_PRIVATE) != 0) {
        methodDescription.append("private ");
    }

    if ((methodNode.access & Opcodes.ACC_PROTECTED) != 0) {
        methodDescription.append("protected ");
    }

    if ((methodNode.access & Opcodes.ACC_STATIC) != 0) {
        methodDescription.append("static ");
    }

    if ((methodNode.access & Opcodes.ACC_ABSTRACT) != 0) {
        methodDescription.append("abstract ");
    }

    if ((methodNode.access & Opcodes.ACC_SYNCHRONIZED) != 0) {
        methodDescription.append("synchronized ");
    }

    methodDescription.append(returnType.getClassName());
    methodDescription.append(" ");
    methodDescription.append(methodNode.name);

    methodDescription.append("(");
    for (int i = 0; i < argumentTypes.length; i++) {
        Type argumentType = argumentTypes[i];
        if (i > 0) {
            methodDescription.append(", ");
        }
        methodDescription.append(argumentType.getClassName());
    }
    methodDescription.append(")");

    if (!thrownInternalClassNames.isEmpty()) {
        methodDescription.append(" throws ");
        int i = 0;
        for (String thrownInternalClassName : thrownInternalClassNames) {
            if (i > 0) {
                methodDescription.append(", ");
            }
            methodDescription.append(Type.getObjectType(thrownInternalClassName).getClassName());
            i++;
        }
    }

    return methodDescription.toString();
}
别念他 2024-09-05 15:55:19

我能想到的最好的方法是使用 ASM 字节码框架。那么至少您不必使用行解析器进行一些反编译输出。事实上,获取方法存根应该类似于其访问者接口之一的 20 行实现。

我自己用它来重写字节码,它相当简单直接(特别是如果您只是阅读类文件)。

http://asm.ow2.org/

The best way I can think of is to use the ASM-bytecode framework. Then at least you wouldn't have to go through some decompilation-output with a line-parser. In fact, getting the method-stubs should be like a 20-line implementation of one of their Visitor-interfaces.

I've used it myself for bytecode rewriting and it's fairly simple and straight forward (especially if you're just reading the class-files).

http://asm.ow2.org/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文