代码之家  ›  专栏  ›  技术社区  ›  Sean Patrick Floyd

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

  •  11
  • Sean Patrick Floyd  · 技术社区  · 14 年前

    更简单的是:没有通配符导入( import com.mycompany.api.*; )在我的项目中,没有完全限定的字段或变量定义( private com.mycompany.api.MyThingy thingy; )也没有 Class.forName(...)

    8 回复  |  直到 9 年前
        1
  •  13
  •   Sean Patrick Floyd    14 年前

    您可以使用 ASM Remapper 班级(信不信由你)。这个类实际上是用来替换字节码中出现的所有类名。不过,就您的目的而言,它不需要替换任何东西。

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

    重新映射 mapType(String) 方法,记录其参数以供以后使用。

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

    现在可以编写如下方法:

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

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


    由seanizer编辑(OP)

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

    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
    
        3
  •  2
  •   emory    14 年前
    1. Compiler Tree API
    2. 字节码分析-完全限定名应该在常量池中
        4
  •  1
  •   aioobe    14 年前

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

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

        5
  •  1
  •   Grodriguez    14 年前

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

    我已经在我的构建过程中集成了它,以便检查应用程序是否没有使用某些api。

        6
  •  0
  •   Sean Patrick Floyd    14 年前

    您可能想使用 STAN 为了这个。

        7
  •  0
  •   L. Holanda    14 年前

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

    alt text

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

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

        8
  •  0
  •   Asraful Haque    13 年前

    谢谢亚当·佩恩特,它帮助了我。但是我想要的是(递归地)获取依赖类,这意味着从项目中获取一个特性。所以,需要得到与特定类相关联的所有类,以及这些类所使用的类等等。还有罐子。所以,我创造了我自己的 Java Dependency Resolver project 它将查找项目中特定类的依赖类/jar。我在这里分享它,它可能对任何人都有用。