Java 找出使用了给定API的哪些类

Java 找出使用了给定API的哪些类,java,reflection,import,bytecode,Java,Reflection,Import,Bytecode,在我的一个Java项目中,我想通过编程找出使用给定API中的哪些类。有什么好办法吗?可能是通过源代码解析还是字节码解析?因为我担心反思没有任何用处 让事情更简单:我的项目中没有通配符导入(import com.mycompany.api.*;),没有完全限定的字段或变量定义(private com.mycompany.api.MyThingy;),也没有任何构造。考虑到这些限制,我想它可以归结为解析导入语句。有没有更好的方法可以做到这一点?我认为以下几点可能会对您有所帮助: 也许是这样的:

在我的一个Java项目中,我想通过编程找出使用给定API中的哪些类。有什么好办法吗?可能是通过源代码解析还是字节码解析?因为我担心反思没有任何用处


让事情更简单:我的项目中没有通配符导入(
import com.mycompany.api.*;
),没有完全限定的字段或变量定义(
private com.mycompany.api.MyThingy;
),也没有任何构造。考虑到这些限制,我想它可以归结为解析导入语句。有没有更好的方法可以做到这一点?

我认为以下几点可能会对您有所帮助:


  • 也许是这样的:

    import java.io.*;
    import java.util.Scanner;
    import java.util.regex.Pattern;
    
    public class FileTraverser {
    
        public static void main(String[] args) {
            visitAllDirsAndFiles(new File("source_directory"));
        }
    
        public static void visitAllDirsAndFiles(File root) {
            if (root.isDirectory())
                for (String child : root.list())
                    visitAllDirsAndFiles(new File(root, child));
            process(root);
        }
    
        private static void process(File f) {
    
            Pattern p = Pattern.compile("(?=\\p{javaWhitespace}*)import (.*);");
            if (f.isFile() && f.getName().endsWith(".java")) {
                try {
                    Scanner s = new Scanner(f);
                    String cls = "";
                    while (null != (cls = s.findWithinHorizon(p, 0)))
                        System.out.println(cls);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    你可能想把评论考虑进去,但这并不难。您还可以确保只在类声明之前查找导入。

    您可以使用的类发现类(信不信由你)。这个类实际上是用来替换字节码中出现的所有类名。但是,出于您的目的,它不需要替换任何东西

    这可能没有什么意义,所以这里有一个例子

    首先,创建
    Remapper
    的子类,该子类的唯一用途是拦截对该方法的所有调用,记录其参数以供以后使用

    public class ClassNameRecordingRemapper extends Remapper {
    
        private final Set<? super String> classNames;
    
        public ClassNameRecordingRemapper(Set<? super String> classNames) {
            this.classNames = classNames;
        }
    
        @Override
        public String mapType(String type) {
            classNames.add(type);
            return type;
        }
    
    }
    
  • 字节码分析-完全限定名应位于常量池中
  • 我正是为了这个目的而使用的。它可以分析字节码并提取所有依赖项,然后以txt或xml格式转储报告(请参阅DependencyExtractor工具)。您应该能够从应用程序的代码中以编程方式分析报告

    我在构建过程中集成了这一功能,以检查应用程序是否未使用某些API。

    您可能需要使用它


    “耦合视图”将API的依赖关系可视化为一个漂亮的图形。

    如果您使用Eclipse。尝试使用分析工具。它不仅告诉我们正在使用哪些类,还告诉我们更多关于它的信息。结果将类似于:

    以下是一个非常好的快速入门:


    谢谢亚当·潘特,它帮助了我。但我所寻找的是(递归地)获取依赖类——这意味着从项目中获取特性。因此,需要获取与特定类关联的所有类,以及这些类的已用类,依此类推。还有罐子。因此,我创建了自己的,它将为项目中的特定类查找依赖类/JAR。我在这里分享它,它可能对某些主体有任何用处。

    Bash脚本(例如使用egrep)不是一个选项?不是真的。a) 我需要java代码,b)它需要在windows上运行,*xa)感谢您的回答b)我认为这两种方法都没有帮助,因为我想根据结果自动运行一些java代码。Dependency Finder是开源的。您可以在项目中签出并集成代码,也可以对生成的报告进行文本分析。感谢您的回答(+1),但这里列出的几种技术完全符合我的要求,因此我不会检查关闭调用。我目前正在使用bcel进行字节码分析,但这很痛苦。我更喜欢编译器树API,但起点是什么?(我没有编译任务,也没有ProcessingEnvironment)你可以在你的源代码上运行注释处理器来获得一个ProcessingEnvironment我知道如何启动注释处理器,但我不想在编译过程中这样做,我不知道如何在不使用编译过程的情况下使用编译器树API。这可能不适合你。听起来正是我要找的,我来看看,谢谢(+1)看起来很棒!我特别喜欢输出。我刚刚意识到,您收到的“类名”似乎是内部类名。ASM提供类来帮助您完成此任务。例如,您可以使用
    Type Type=Type.getType(internalClassName)
    。从这里,您可以调用
    type.getClassName()
    ,这是您习惯使用的类名。@Bohzo:这是一个有趣的解决Stack Overflow缺少消息传递系统的方法!:)我希望你们能得到这个答案。我喜欢这个,但它忽略了方法中使用的类;有没有什么办法也能找到那些参考资料?很酷。看起来空的查看器已被删除。用什么来代替呢?我将对此进行投票,因为这是一个很好的答案,但我不认为DependencyFinder和我可以成为朋友:-)这正是我自己编写解决方案的方式(+1表示:-),但我正在寻找一个真正理解源代码或字节码的解决方案。很好,但不是我问题的答案,我需要一个程序化的方法来获取使用过的类,而不是一个漂亮的报告。下次请仔细阅读问题和其他答案。我在寻找可以通过编程使用的东西。你所建议的是一个很好的工具,但不是我问题的答案。
    public Set<String> findClassNames(byte[] bytecode) {
        Set<String> classNames = new HashSet<String>();
    
        ClassReader classReader = new ClassReader(bytecode);
        ClassWriter classWriter = new ClassWriter(classReader, 0);
    
        ClassNameRecordingRemapper remapper = new ClassNameRecordingRemapper(classNames);
        classReader.accept(remapper, 0);
    
        return classNames;
    }
    
    public static class Collector extends Remapper{
    
        private final Set<Class<?>> classNames;
        private final String prefix;
    
        public Collector(final Set<Class<?>> classNames, final String prefix){
            this.classNames = classNames;
            this.prefix = prefix;
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public String mapDesc(final String desc){
            if(desc.startsWith("L")){
                this.addType(desc.substring(1, desc.length() - 1));
            }
            return super.mapDesc(desc);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public String[] mapTypes(final String[] types){
            for(final String type : types){
                this.addType(type);
            }
            return super.mapTypes(types);
        }
    
        private void addType(final String type){
            final String className = type.replace('/', '.');
            if(className.startsWith(this.prefix)){
                try{
                    this.classNames.add(Class.forName(className));
                } catch(final ClassNotFoundException e){
                    throw new IllegalStateException(e);
                }
            }
        }
    
        @Override
        public String mapType(final String type){
            this.addType(type);
            return type;
        }
    
    }
    
    public static Set<Class<?>> getClassesUsedBy(
        final String name,   // class name
        final String prefix  // common prefix for all classes
                             // that will be retrieved
        ) throws IOException{
        final ClassReader reader = new ClassReader(name);
        final Set<Class<?>> classes =
            new TreeSet<Class<?>>(new Comparator<Class<?>>(){
    
                @Override
                public int compare(final Class<?> o1, final Class<?> o2){
                    return o1.getName().compareTo(o2.getName());
                }
            });
        final Remapper remapper = new Collector(classes, prefix);
        final ClassVisitor inner = new EmptyVisitor();
        final RemappingClassAdapter visitor =
            new RemappingClassAdapter(inner, remapper);
        reader.accept(visitor, 0);
        return classes;
    }
    
    public static void main(final String[] args) throws Exception{
        final Collection<Class<?>> classes =
            getClassesUsedBy(Collections.class.getName(), "java.util");
        System.out.println("Used classes:");
        for(final Class<?> cls : classes){
            System.out.println(" - " + cls.getName());
        }
    
    }
    
    Used classes:
     - java.util.ArrayList
     - java.util.Arrays
     - java.util.Collection
     - java.util.Collections
     - java.util.Collections$1
     - java.util.Collections$AsLIFOQueue
     - java.util.Collections$CheckedCollection
     - java.util.Collections$CheckedList
     - java.util.Collections$CheckedMap
     - java.util.Collections$CheckedRandomAccessList
     - java.util.Collections$CheckedSet
     - java.util.Collections$CheckedSortedMap
     - java.util.Collections$CheckedSortedSet
     - java.util.Collections$CopiesList
     - java.util.Collections$EmptyList
     - java.util.Collections$EmptyMap
     - java.util.Collections$EmptySet
     - java.util.Collections$ReverseComparator
     - java.util.Collections$ReverseComparator2
     - java.util.Collections$SelfComparable
     - java.util.Collections$SetFromMap
     - java.util.Collections$SingletonList
     - java.util.Collections$SingletonMap
     - java.util.Collections$SingletonSet
     - java.util.Collections$SynchronizedCollection
     - java.util.Collections$SynchronizedList
     - java.util.Collections$SynchronizedMap
     - java.util.Collections$SynchronizedRandomAccessList
     - java.util.Collections$SynchronizedSet
     - java.util.Collections$SynchronizedSortedMap
     - java.util.Collections$SynchronizedSortedSet
     - java.util.Collections$UnmodifiableCollection
     - java.util.Collections$UnmodifiableList
     - java.util.Collections$UnmodifiableMap
     - java.util.Collections$UnmodifiableRandomAccessList
     - java.util.Collections$UnmodifiableSet
     - java.util.Collections$UnmodifiableSortedMap
     - java.util.Collections$UnmodifiableSortedSet
     - java.util.Comparator
     - java.util.Deque
     - java.util.Enumeration
     - java.util.Iterator
     - java.util.List
     - java.util.ListIterator
     - java.util.Map
     - java.util.Queue
     - java.util.Random
     - java.util.RandomAccess
     - java.util.Set
     - java.util.SortedMap
     - java.util.SortedSet