通过Java从.class文件获取字节码(依赖项)信息

通过Java从.class文件获取字节码(依赖项)信息,java,java-8,bytecode,jdeps,Java,Java 8,Bytecode,Jdeps,我想分析.class文件并获得关于哪个类使用哪个其他类的信息 jdeps是一个命令行工具,它允许您在控制台中显示一些信息,但我希望避免调用外部工具并删除命令行输出 所有依赖项都记录在类文件的中心位置,即常量池。因此,为了有效地处理所有依赖项,您需要一个库,该库允许在不查看类文件其余部分的情况下处理常量池,从而排除ASM,否则ASM是一个非常好的字节码处理库 因此,使用,例如,您可以按照 private static Set<String> getDependencies(InputS

我想分析.class文件并获得关于哪个类使用哪个其他类的信息


jdeps是一个命令行工具,它允许您在控制台中显示一些信息,但我希望避免调用外部工具并删除命令行输出

所有依赖项都记录在类文件的中心位置,即常量池。因此,为了有效地处理所有依赖项,您需要一个库,该库允许在不查看类文件其余部分的情况下处理常量池,从而排除ASM,否则ASM是一个非常好的字节码处理库

因此,使用,例如,您可以按照

private static Set<String> getDependencies(InputStream is) throws IOException {
    ClassFile cf = new ClassFile(new DataInputStream(is));
    ConstPool constPool = cf.getConstPool();
    HashSet<String> set = new HashSet<>();
    for(int ix = 1, size = constPool.getSize(); ix < size; ix++) {
        int descriptorIndex;
        switch (constPool.getTag(ix)) {
            case ConstPool.CONST_Class: set.add(constPool.getClassInfo(ix));
            default: continue;
            case ConstPool.CONST_NameAndType:
                descriptorIndex = constPool.getNameAndTypeDescriptor(ix);
                break;
            case ConstPool.CONST_MethodType:
                descriptorIndex = constPool.getMethodTypeInfo(ix);
        }
        String desc = constPool.getUtf8Info(descriptorIndex);
        for(int p = 0; p<desc.length(); p++)
            if(desc.charAt(p)=='L')
                set.add(desc.substring(++p, p = desc.indexOf(';', p)).replace('/', '.'));
    }
    return set;
}
显示

[B] [C] [我 [Ljava.lang.CharSequence; [Ljava.lang.String; java.io.ObjectStreamField java.io.Serializable java.io.UnsupportedEncodingException java.lang.AbstractStringBuilder java.lang.CharSequence java.lang.Character java.lang.com java.lang.Double java.lang.Float java.lang.IndexOutOfBoundsException java.lang.Integer java.lang.Iterable java.lang.Long java.lang.Math java.lang.NullPointerException java.lang.Object java.lang.OutOfMemoryError java.lang.String java.lang.String$1 java.lang.String$CaseInsensitiveComparator java.lang.StringBuffer java.lang.StringBuilder java.lang.StringCodeing java.lang.StringCoding$Result java.lang.StringIndexOutOfBoundsException java.lang.StringLatin1 java.lang.StringLatin1$CharsSpliterator java.lang.StringUTF16 java.lang.StringUTF16$CharsSpliterator java.lang.StringUTF16$CodePointsSpliterator java.lang.System java.lang.Throwable java.lang.Void java.nio.charset.charset java.util.ArrayList java.util.Array java.util.Comparator java.util.Formatter java.util.Iterator java.util.List java.util.Locale java.util.Objects java.util.Spliterator java.util.Spliterator$OfInt java.util.StringJoiner java.util.regex.Matcher java.util.regex.Pattern java.util.stream.IntStream java.util.stream.StreamSupport 关于Java9

您可以使用以下方法获得相同的结果:


所有依赖项都记录在类文件的中心位置,即常量池。因此,为了有效地处理所有依赖项,您需要一个库,该库允许处理常量池,而无需查看类文件的其余部分,这就排除了ASM,否则ASM是一个非常好的字节码处理库

因此,使用,例如,您可以按照

private static Set<String> getDependencies(InputStream is) throws IOException {
    ClassFile cf = new ClassFile(new DataInputStream(is));
    ConstPool constPool = cf.getConstPool();
    HashSet<String> set = new HashSet<>();
    for(int ix = 1, size = constPool.getSize(); ix < size; ix++) {
        int descriptorIndex;
        switch (constPool.getTag(ix)) {
            case ConstPool.CONST_Class: set.add(constPool.getClassInfo(ix));
            default: continue;
            case ConstPool.CONST_NameAndType:
                descriptorIndex = constPool.getNameAndTypeDescriptor(ix);
                break;
            case ConstPool.CONST_MethodType:
                descriptorIndex = constPool.getMethodTypeInfo(ix);
        }
        String desc = constPool.getUtf8Info(descriptorIndex);
        for(int p = 0; p<desc.length(); p++)
            if(desc.charAt(p)=='L')
                set.add(desc.substring(++p, p = desc.indexOf(';', p)).replace('/', '.'));
    }
    return set;
}
显示

[B] [C] [我 [Ljava.lang.CharSequence; [Ljava.lang.String; java.io.ObjectStreamField java.io.Serializable java.io.UnsupportedEncodingException java.lang.AbstractStringBuilder java.lang.CharSequence java.lang.Character java.lang.com java.lang.Double java.lang.Float java.lang.IndexOutOfBoundsException java.lang.Integer java.lang.Iterable java.lang.Long java.lang.Math java.lang.NullPointerException java.lang.Object java.lang.OutOfMemoryError java.lang.String java.lang.String$1 java.lang.String$CaseInsensitiveComparator java.lang.StringBuffer java.lang.StringBuilder java.lang.StringCodeing java.lang.StringCoding$Result java.lang.StringIndexOutOfBoundsException java.lang.StringLatin1 java.lang.StringLatin1$CharsSpliterator java.lang.StringUTF16 java.lang.StringUTF16$CharsSpliterator java.lang.StringUTF16$CodePointsSpliterator java.lang.System java.lang.Throwable java.lang.Void java.nio.charset.charset java.util.ArrayList java.util.Array java.util.Comparator java.util.Formatter java.util.Iterator java.util.List java.util.Locale java.util.Objects java.util.Spliterator java.util.Spliterator$OfInt java.util.StringJoiner java.util.regex.Matcher java.util.regex.Pattern java.util.stream.IntStream java.util.stream.StreamSupport 关于Java9

您可以使用以下方法获得相同的结果:


你不能从IDE上做到这一点吗?只是想知道…似乎也能做到,但我还没有试着感谢你的提示。这是关于以编程方式分析50000个Java工件。AFAIK javap没有编程访问权限,但当然你可以使用一个进程来调用它,然后解析输出;看起来简单的grep就可以在您的情况下,只需搜索该类就足够了。我只是想知道继承是否会有任何问题……我喜欢这个问题,但您不能从IDE执行此操作?我只是想知道……似乎也可以执行此操作,但我还没有尝试过感谢您的提示。这是关于分析50000个Java工件的编程AFAIK javap没有编程访问权限,但是你当然可以使用一个进程来访问它,然后调用它,然后解析输出;在你的例子中,简单的grep就足以简单地搜索那个类了。我只是想知道继承是否会有任何问题……我喜欢这个问题看看我真的很希望g你会回答这个问题的!谢谢。前五个带[开头]的条目是什么意思?@JFMeier这些名称的格式是[B、[C、[I表示字节[]、字符[]、和int[]。[Ljava.lang.CharSequence;和[L]
java.lang.String;请参阅字符序列[]和字符串[]。基本上,它是JVM的内部名称,但是/替换为..我真的很希望你能回答这个问题!非常感谢。前五个条目的开头是什么意思?@JFMeier这些名称的格式是[B、[C、[I表示byte[]、char[]和int[]。[Ljava.lang.CharSequence;和[Ljava.lang.String;参考CharSequence[]和String[]。基本上,它是JVM的内部名称,但/替换为。。
private static Set<String> getDependencies(InputStream is) throws IOException {
    JavaClass cf = new ClassParser(is, "").parse();
    ConstantPool constPool = cf.getConstantPool();
    HashSet<String> set = new HashSet<>();
    constPool.accept(new DescendingVisitor(cf, new EmptyVisitor() {
        @Override public void visitConstantClass(ConstantClass cC) {
            set.add(((String)cC.getConstantValue(constPool)).replace('/', '.'));
        }
        @Override public void visitConstantNameAndType(ConstantNameAndType cNaT) {
            processSignature(cNaT.getSignature(constPool));
        }
        @Override public void visitConstantMethodType(ConstantMethodType cMt) {
            processSignature(
                constPool.constantToString(cMt.getDescriptorIndex(),
                (byte)ConstPool.CONST_Utf8));
        }
        private void processSignature(String desc) {
            for(int p = 0; p<desc.length(); p++)
                if(desc.charAt(p)=='L')
                    set.add(desc.substring(++p, p=desc.indexOf(';', p)).replace('/', '.'));
        }
    }));
    return set;
}