为了简化事情:在我的项目中没有通配符导入(
import com.mycompany.api.*;
),也没有完全限定的字段或变量定义(private com.mycompany.api.MyThingy thingy;
)或任何Class.forName(...)
结构。考虑到这些限制,我想这就归结为解析导入语句了。有没有首选的方法来做到这一点?import com.mycompany.api.*;
),也没有完全限定的字段或变量定义(private com.mycompany.api.MyThingy thingy;
)或任何Class.forName(...)
结构。考虑到这些限制,我想这就归结为解析导入语句了。有没有首选的方法来做到这一点?你可以使用ASM的Remapper
类来发现类(信不信由你)。实际上,这个类的作用是在字节码中替换所有出现的类名。但是对于你的目的,它不需要替换任何东西。
这可能听起来有点难以理解,所以这里有一个例子...
首先,你需要创建一个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;
}
实际上,获取所有类的字节码是您的责任。
我接受了这个答案,但由于上面的代码不完全正确,我将插入我使用的方法:
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
Type
类来帮助您解决这个问题。例如,您可以使用Type type = Type.getType(internalClassName)
。从这里开始,您可以调用type.getClassName()
,这就是您所熟悉的类名。 - Adam Paynter也许是这样的:
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之间的依赖关系。
这里有一个非常好的快速入门指南:
http://www.eclipse.org/tptp/home/documents/tutorials/profilingtool/profilingexample_32.html
感谢Adam Paynter,这很有帮助。但是我要找的是递归获取依赖类-也就是从一个项目中提取特性。因此,需要获取与特定类相关联的所有类,以及这些类的使用类等等。还要获取JAR文件。因此,我创建了自己的Java依赖关系解析器项目,该项目将为项目中的特定类查找依赖类/ JAR。我在这里分享它,可以为某些人提供帮助。