本文介绍了找出使用给定API的哪些类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In a Java Project of mine, I would like to find out programmatically which classes from a given API are used. Is there a good way to do that? Through source code parsing or bytecode parsing maybe? Because Reflection won't be of any use, I'm afraid.

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

To make things simpler: there are no wildcard imports (import com.mycompany.api.*;) anywhere in my project, no fully qualified field or variable definitions (private com.mycompany.api.MyThingy thingy;) nor any Class.forName(...) constructs. Given these limitations, it boils down to parsing import statements, I guess. Is there a preferred approach to do this?

推荐答案

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

You can discover the classes using ASM's Remapper class (believe it or not). This class is actually meant to replace all occurrences of a class name within bytecode. For your purposes, however, it doesn't need to replace anything.

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

This probably doesn't make a whole lot of sense, so here is an example...

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

First, you create a subclass of Remapper whose only purpose in life is to intercept all calls to the mapType(String) method, recording its argument for later use.

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

}

现在你可以编写类似的方法这个:

Now you can write a method like this:

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

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

It's your responsibility to actually obtain all classes' bytecode.

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

I am accepting this answer, but as the above code is not quite correct, I will insert the way I used this:

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

这是一个使用以下方法测试它的主要类:

Here's a main class to test it using:

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

这篇关于找出使用给定API的哪些类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-23 08:29