找出使用给定API的哪些类

在我的Java项目中,我想以编程方式找出使用给定API中的哪些类。 有没有一个好方法呢? 通过源代码解析或字节码解析可能吗? 因为reflection不会有任何用处,我担心。

为了简单起见:我的项目中没有任何通配符导入( import com.mycompany.api.*; ),没有完全限定的字段或变量定义( private com.mycompany.api.MyThingy thingy; )也没有任何Class.forName(...)构造。 鉴于这些限制,我认为它可以归结为解析import语句。 有没有一种首选的方法来做到这一点?

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

这可能没有多大意义,所以这里有一个例子……

首先,创建Remapper的子类,其唯一目的是拦截对mapType(String)方法的所有调用,记录其参数以供以后使用。

 public class ClassNameRecordingRemapper extends Remapper { private final Set classNames; public ClassNameRecordingRemapper(Set classNames) { this.classNames = classNames; } @Override public String mapType(String type) { classNames.add(type); return type; } } 

现在你可以写一个像这样的方法:

 public Set findClassNames(byte[] bytecode) { Set classNames = new HashSet(); ClassReader classReader = new ClassReader(bytecode); ClassWriter classWriter = new ClassWriter(classReader, 0); ClassNameRecordingRemapper remapper = new ClassNameRecordingRemapper(classNames); classReader.accept(remapper, 0); return classNames; } 

实际获取所有类的字节码是你的责任。


由seanizer编辑(OP)

我接受这个答案,但由于上面的代码不太正确,我将插入我使用的方式:

 public static class Collector extends Remapper{ private final Set> classNames; private final String prefix; public Collector(final Set> 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> 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> classes = new TreeSet>(new Comparator>(){ @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> 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 

我认为以下内容可能对您有所帮助:

  1. 类依赖性分析器
  2. 依赖性查找器
  1. 编译器树API
  2. 字节码分析 – 完全限定名称应该在常量池中

也许这样的事情:

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

您可能想要考虑评论,但这不应该太难。 您还可以确保在类声明之前只查找导入。

我完全为此目的使用DependencyFinder 。 它可以分析字节码并提取所有依赖关系,然后以txt或xml格式转储报告(请参阅DependencyExtractor工具)。 您应该能够以编程方式从应用程序的代码中分析报告。

我已经在构建过程中集成了这个,以便检查应用程序是否未使用某些API。

你可能想要使用STAN 。

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

如果您使用Eclipse。 尝试使用分析工具。 它不仅告诉我们正在使用哪些类,而且还会详细介绍它。 结果将是这样的:

替代文字

有一个非常好的快速启动:

http://www.eclipse.org/tptp/home/documents/tutorials/profilingtool/profilingexample_32.html

感谢Adam Paynter,它帮助了我。 但我正在寻找的是获取依赖类(递归) – 这意味着从项目中获取一个function。 因此,需要获取与特定类关联的所有类以及这些类的已使用类等等。 还得到了jar子。 因此,我创建了自己的Java Dependency Resolver项目,该项目将为Project中的特定类找到依赖的Classes / jars。 我在这里分享它可能会对某些身体有任何用处。