2010-05-19 44 views
3

我需要編寫一個工具,列出調用指定接口的方法的類。它將用作包含許多模塊的大型Java應用程序的構建過程的一部分。目標是自動記錄某些java模塊之間的依賴關係。ASM方法訪問者是否可以與接口一起使用?

我發現了一些用於依賴關係分析的工具,但它們不能在方法級別上工作,只能用於包或罐子。最後我發現ASM,這似乎是我所需要的。

以下代碼打印所有的類文件中的給定目錄中的方法依賴關係:

import java.io.*; 
import java.util.*; 

import org.objectweb.asm.ClassReader; 

public class Test { 

    public static void main(String[] args) throws Exception { 

     File dir = new File(args[0]); 

     List<File> classFiles = new LinkedList<File>(); 
     findClassFiles(classFiles, dir); 

     for (File classFile : classFiles) { 
      InputStream input = new FileInputStream(classFile); 
      new ClassReader(input).accept(new MyClassVisitor(), 0); 
      input.close(); 
     } 
    } 

    private static void findClassFiles(List<File> list, File dir) { 
     for (File file : dir.listFiles()) { 
      if (file.isDirectory()) { 
       findClassFiles(list, file); 
      } else if (file.getName().endsWith(".class")) { 
       list.add(file); 
      } 
     } 
    } 
} 

import org.objectweb.asm.MethodVisitor; 
import org.objectweb.asm.commons.EmptyVisitor; 

public class MyClassVisitor extends EmptyVisitor { 

    private String className; 

    @Override 
    public void visit(int version, int access, String name, String signature, 
      String superName, String[] interfaces) { 
     this.className = name; 
    } 

    @Override 
    public MethodVisitor visitMethod(int access, String name, String desc, 
      String signature, String[] exceptions) { 

     System.out.println(className + "." + name); 
     return new MyMethodVisitor(); 
    } 
} 

import org.objectweb.asm.commons.EmptyVisitor; 

public class MyMethodVisitor extends EmptyVisitor { 

    @Override 
    public void visitMethodInsn(int opcode, String owner, String name, 
      String desc) { 

     String key = owner + "." + name; 
     System.out.println(" " + key); 
    } 
} 

問題:

該代碼僅適用於普通類!如果類文件包含一個接口,則調用visitMethod,但不會調用visitMethodInsn。我沒有得到關於接口方法調用者的任何信息。

任何想法?

回答

1

我不得不承認,我很困惑...

我認爲ASM遊客做一些魔術給我的 所有調用給定的方法,就像一個堆棧跟蹤的列表。相反,他們只是解析類和方法 機構。幸運的是,這完全滿足我的需求,因爲我可以自己構建 調用樹。

下面的代碼只列出了其他方法調用的所有方法,檢查在給定的目錄類文件(及子目錄):


import java.io.*; 
import java.util.*; 

import org.objectweb.asm.ClassReader; 

public class Test { 

    public static void main(String[] args) throws Exception { 

     File dir = new File(args[0]); 

     Map<String, Set<String>> callMap = new HashMap<String, Set<String>>(); 

     List<File> classFiles = new LinkedList<File>(); 
     findClassFiles(classFiles, dir); 

     for (File classFile : classFiles) { 
      InputStream input = new FileInputStream(classFile); 
      new ClassReader(input).accept(new MyClassVisitor(callMap), 0); 
      input.close(); 
     } 

     for (Map.Entry<String, Set<String>> entry : callMap.entrySet()) { 
      String method = entry.getKey(); 
      Set<String> callers = entry.getValue(); 

      if (callers != null && !callers.isEmpty()) { 
       System.out.println(method); 
       for (String caller : callers) { 
        System.out.println(" " + caller); 
       } 
      } 
     } 
    } 

    private static void findClassFiles(List<File> list, File dir) { 
     for (File file : dir.listFiles()) { 
      if (file.isDirectory()) { 
       findClassFiles(list, file); 
      } else if (file.getName().endsWith(".class")) { 
       list.add(file); 
      } 
     } 
    } 
} 

import java.util.*; 

import org.objectweb.asm.MethodVisitor; 
import org.objectweb.asm.commons.EmptyVisitor; 

public class MyClassVisitor extends EmptyVisitor { 

    private String className; 
    private Map<String, Set<String>> callMap; 

    public MyClassVisitor(Map<String, Set<String>> callMap) { 
     this.callMap = callMap; 
    } 

    @Override 
    public void visit(int version, int access, String name, String signature, 
      String superName, String[] interfaces) { 
     this.className = name; 
    } 

    @Override 
    public MethodVisitor visitMethod(int access, String name, String desc, 
      String signature, String[] exceptions) { 

     return new MyMethodVisitor(className + "." + name, callMap); 
    } 
} 

import java.util.*; 

import org.objectweb.asm.commons.EmptyVisitor; 

public class MyMethodVisitor extends EmptyVisitor { 

    private String currentMethod; 
    private Map<String, Set<String>> callMap; 

    public MyMethodVisitor(String currentMethod, 
      Map<String, Set<String>> callMap) { 
     this.currentMethod = currentMethod; 
     this.callMap = callMap; 
    } 

    @Override 
    public void visitMethodInsn(int opcode, String owner, String name, 
      String desc) { 

     String calledMethod = owner + "." + name; 

     Set<String> callers = callMap.get(calledMethod); 
     if (callers == null) { 
      callers = new TreeSet<String>(); 
      callMap.put(calledMethod, callers); 
     } 

     callers.add(currentMethod); 
    } 
} 
1

我認爲這是因爲接口方法沒有方法體。嘗試編寫一個空方法作爲「普通」類的一部分,並查看是否調用了visitMethodInsn。

順便說一句,您是否考慮過使用java.lang.instrument來發現在運行時加載的類,並以此方式執行您的檢測,而不是從磁盤讀取類文件?

+0

我不認爲在這種情況下有一個方法體是相關的。因爲調用了* visitMethod()*並返回了一個新的* MyMethodVisitor *實例,所以訪問該接口方法,但此* MyMethodVisitor *之後似乎被忽略。 我從磁盤讀取類,因爲我需要分析一個沒有運行的應用程序。 – 2010-05-20 09:02:31

相關問題