我需要获取Java中感兴趣的方法的所有调用方法的列表。有没有可以帮助我的工具?
编辑:我忘了提到我需要从程序中执行此操作。我使用Java Pathfinder,我想运行它来调用我感兴趣的方法的所有方法。
答案 0 :(得分:43)
为了分析字节码,我建议ASM。给定要分析的类列表,可以创建一个查找您感兴趣的方法调用的访问者。下面是一个分析jar文件中的类的实现。
请注意,ASM使用internalNames和'/'而不是'。'作为分隔符。将目标方法指定为standard declaration,不带修饰符。
例如,列出可以在java运行时jar中调用System.out.println(“foo”)的方法:
java -cp "classes;asm-3.1.jar;asm-commons-3.1.jar" App \
c:/java/jdk/jre/lib/rt.jar \
java/io/PrintStream "void println(String)"
编辑:添加了源代码和行号:请注意,这仅表示每个调用方法的最后一个目标方法调用 - 原始q只想知道哪些方法。我把它作为练习让读者显示调用方法声明的行号,或每个目标调用的行号,具体取决于你实际使用的内容。 :)
结果:
LogSupport.java:44 com/sun/activation/registries/LogSupport log (Ljava/lang/String;)V
LogSupport.java:50 com/sun/activation/registries/LogSupport log (Ljava/lang/String;Ljava/lang/Throwable;)V
...
Throwable.java:498 java/lang/Throwable printStackTraceAsCause (Ljava/io/PrintStream;[Ljava/lang/StackTraceElement;)V
--
885 methods invoke java/io/PrintStream println (Ljava/lang/String;)V
源:
public class App {
private String targetClass;
private Method targetMethod;
private AppClassVisitor cv;
private ArrayList<Callee> callees = new ArrayList<Callee>();
private static class Callee {
String className;
String methodName;
String methodDesc;
String source;
int line;
public Callee(String cName, String mName, String mDesc, String src, int ln) {
className = cName; methodName = mName; methodDesc = mDesc; source = src; line = ln;
}
}
private class AppMethodVisitor extends MethodAdapter {
boolean callsTarget;
int line;
public AppMethodVisitor() { super(new EmptyVisitor()); }
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(targetClass)
&& name.equals(targetMethod.getName())
&& desc.equals(targetMethod.getDescriptor())) {
callsTarget = true;
}
}
public void visitCode() {
callsTarget = false;
}
public void visitLineNumber(int line, Label start) {
this.line = line;
}
public void visitEnd() {
if (callsTarget)
callees.add(new Callee(cv.className, cv.methodName, cv.methodDesc,
cv.source, line));
}
}
private class AppClassVisitor extends ClassAdapter {
private AppMethodVisitor mv = new AppMethodVisitor();
public String source;
public String className;
public String methodName;
public String methodDesc;
public AppClassVisitor() { super(new EmptyVisitor()); }
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
className = name;
}
public void visitSource(String source, String debug) {
this.source = source;
}
public MethodVisitor visitMethod(int access, String name,
String desc, String signature,
String[] exceptions) {
methodName = name;
methodDesc = desc;
return mv;
}
}
public void findCallingMethodsInJar(String jarPath, String targetClass,
String targetMethodDeclaration) throws Exception {
this.targetClass = targetClass;
this.targetMethod = Method.getMethod(targetMethodDeclaration);
this.cv = new AppClassVisitor();
JarFile jarFile = new JarFile(jarPath);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
InputStream stream = new BufferedInputStream(jarFile.getInputStream(entry), 1024);
ClassReader reader = new ClassReader(stream);
reader.accept(cv, 0);
stream.close();
}
}
}
public static void main( String[] args ) {
try {
App app = new App();
app.findCallingMethodsInJar(args[0], args[1], args[2]);
for (Callee c : app.callees) {
System.out.println(c.source+":"+c.line+" "+c.className+" "+c.methodName+" "+c.methodDesc);
}
System.out.println("--\n"+app.callees.size()+" methods invoke "+
app.targetClass+" "+
app.targetMethod.getName()+" "+app.targetMethod.getDescriptor());
} catch(Exception x) {
x.printStackTrace();
}
}
}
答案 1 :(得分:12)
编辑:编辑原始问题以指示需要运行时解决方案 - 在编辑之前给出了此答案,并且仅指示在开发期间如何执行此操作。
如果您使用的是Eclipse,则可以右键单击该方法并选择“Open call hierarchy”以获取此信息。
阅读评论后更新:其他IDE也以类似方式支持此功能(至少Netbeans和IntelliJ支持)
答案 2 :(得分:5)
使用@Deprecated注释方法(或使用@deprecated标记),打开弃用警告,运行编译并查看触发的警告。
运行编译位可以通过使用Java 6 compiler API调用外部ant进程或来完成。
答案 3 :(得分:5)
这会弹出一个面板,显示对此功能的所有引用。 Eclipse FTW!
答案 4 :(得分:4)
在eclipse中,突出显示方法名称,然后 Ctrl + Shift + G
答案 5 :(得分:3)
没有办法通过Java反射库(以编程方式)执行此操作 - 您不能要求java.lang.reflect.Method“您调用哪些方法?”
这留下了我能想到的另外两个选择:
源代码的静态分析。我确信这就是Eclipse Java工具集所做的事情 - 您可以查看JDT背后的Eclipse源代码,并在您向Eclipse请求方法的“查找引用”时查找它的作用。
字节码分析。您可以检查字节码以获取对方法的调用。我不确定哪些库或示例可以帮助解决这个问题 - 但我无法想象某些东西不存在。
答案 6 :(得分:1)
是的,大多数现代IDE:s都会让你搜索方法或变量的用法。或者,您可以使用调试器并在方法条目上设置跟踪点,在每次调用方法时打印堆栈跟踪或其他任何内容。 最后,您可以使用一些简单的shell util来为该方法设置grep,例如
find . -name '*.java' -exec grep -H methodName {} ;
但是,通过某种反射方法找到调用的唯一方法是使用调试器。
答案 7 :(得分:1)
1)在eclipse中,它是 - &gt;右键单击该方法并选择打开调用层次结构或CLT+ALT+H
2)在jdeveloper中它是 - &gt;右键单击该方法并选择通话或ALT+SHIFT+H
答案 8 :(得分:0)
我能找到的最接近的是此StackOverflow中描述的方法问题所选答案。check this out
答案 9 :(得分:0)
我用@ Chadwick的一个小例子。这是一个测试,评估是否通过实现@Transaction的方法调用getDatabaseEngine()。
/**
* Ensures that methods that call {@link DatabaseProvider#getDatabaseEngine()}
* implement the {@link @Transaction} annotation.
*
* @throws Exception If something occurs while testing.
*/
@Test
public void ensure() throws Exception {
final Method method = Method.getMethod(
DatabaseEngine.class.getCanonicalName() + " getDatabaseEngine()");
final ArrayList<java.lang.reflect.Method> faultyMethods = Lists.newArrayList();
for (Path p : getAllClasses()) {
try (InputStream stream = new BufferedInputStream(Files.newInputStream(p))) {
ClassReader reader = new ClassReader(stream);
reader.accept(new ClassAdapter(new EmptyVisitor()) {
@Override
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
return new MethodAdapter(new EmptyVisitor()) {
@Override
public void visitMethodInsn(int opcode, String owner, String nameCode, String descCode) {
try {
final Class<?> klass = Class.forName(Type.getObjectType(owner).getClassName());
if (DatabaseProvider.class.isAssignableFrom(klass) &&
nameCode.equals(method.getName()) &&
descCode.equals(method.getDescriptor())) {
final java.lang.reflect.Method method = klass.getDeclaredMethod(name,
getParameters(desc).toArray(new Class[]{}));
for (Annotation annotation : method.getDeclaredAnnotations()) {
if (annotation.annotationType().equals(Transaction.class)) {
return;
}
}
faultyMethods.add(method);
}
} catch (Exception e) {
Throwables.propagate(e);
}
}
};
}
}, 0);
}
}
if (!faultyMethods.isEmpty()) {
fail("\n\nThe following methods must implement @Transaction because they're calling getDatabaseEngine().\n\n" + Joiner.on("\n").join
(faultyMethods) + "\n\n");
}
}
/**
* Gets all the classes from target.
*
* @return The list of classes.
* @throws IOException If something occurs while collecting those classes.
*/
private List<Path> getAllClasses() throws IOException {
final ImmutableList.Builder<Path> builder = new ImmutableList.Builder<>();
Files.walkFileTree(Paths.get("target", "classes"), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
if (file.getFileName().toString().endsWith(".class")) {
builder.add(file);
}
return FileVisitResult.CONTINUE;
}
});
return builder.build();
}
/**
* Gets the list of parameters given the description.
*
* @param desc The method description.
* @return The list of parameters.
* @throws Exception If something occurs getting the parameters.
*/
private List<Class<?>> getParameters(String desc) throws Exception {
ImmutableList.Builder<Class<?>> obj = new ImmutableList.Builder<>();
for (Type type : Type.getArgumentTypes(desc)) {
obj.add(ClassUtils.getClass(type.getClassName()));
}
return obj.build();
}
答案 10 :(得分:-1)
您可以使用IDE中的某些内容执行此操作,例如“查找用法”(这是Netbeans和JDeveloper中所称的内容)。有几点需要注意: