【问题标题】:Java: Easy way to get method stub out of class files within a JAR file? Reflection?Java:从 JAR 文件中的类文件中获取方法存根的简单方法?反射?
【发布时间】:2011-02-11 00:26:50
【问题描述】:

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

有什么想法吗?

亲切的问候

【问题讨论】:

    标签: java reflection class jar methods


    【解决方案1】:

    aioobe's answer 的基础上,您还可以使用 ASM 的树 API(与其访问者 API 不同)来解析 JAR 文件中包含的类文件的内容。同样,您可以使用 JarFile 类读取 JAR 文件中包含的文件。以下是如何做到这一点的示例:

    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();
    }
    

    【讨论】:

    • 这很棒,完全符合我的搜索要求。谢谢你们!
    • 这里是所有导入,使用此代码:import java.io.InputStream; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.objectweb.asm.ClassReader; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; import xbn.lang.SimpleXbnObject;
    • 我没有在网站上找到要导入的好 jar。你能帮忙吗?谢谢。是的,我知道旧帖子,但这个问题很有趣。
    【解决方案2】:

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

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

    http://asm.ow2.org/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-28
      • 1970-01-01
      • 2012-06-29
      • 2012-04-15
      • 1970-01-01
      • 2010-11-06
      • 1970-01-01
      • 2010-11-01
      相关资源
      最近更新 更多