Java ASM方法访问者可以与接口一起使用吗?
我需要编写一个工具,列出调用指定接口方法的类。它将被用作由许多模块组成的大型java应用程序的构建过程的一部分。目标是自动记录某些java模块之间的依赖关系 我发现了几种用于依赖性分析的工具,但它们不适用于方法级别,只适用于包或JAR。最后我找到了ASM,它似乎满足了我的需要 以下代码打印给定目录中所有类文件的方法依赖项:Java ASM方法访问者可以与接口一起使用吗?,java,assembly,static-analysis,java-bytecode-asm,Java,Assembly,Static Analysis,Java Bytecode Asm,我需要编写一个工具,列出调用指定接口方法的类。它将被用作由许多模块组成的大型java应用程序的构建过程的一部分。目标是自动记录某些java模块之间的依赖关系 我发现了几种用于依赖性分析的工具,但它们不适用于方法级别,只适用于包或JAR。最后我找到了ASM,它似乎满足了我的需要 以下代码打印给定目录中所有类文件的方法依赖项: import java.io.*; import java.util.*; import org.objectweb.asm.ClassReader; public cl
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);
}
}
}
}
问题: 该代码仅适用于常规类!如果类文件包含接口,则调用visitMethod,但不调用visitMethodInsn。我没有得到任何关于接口方法调用方的信息
有什么想法吗?我认为这是因为接口方法没有方法体。尝试将空方法作为“普通”类的一部分编写,并查看是否调用了visitMethodInsn
顺便问一下,您是否考虑过使用java.lang.instrument来发现在运行时加载的类并以这种方式进行检测,而不是从磁盘读取类文件?我必须承认,我很困惑… 我想asm访客会做一些魔术,让我知道所有来电者的名单 给定的方法,如stacktrace。相反,它们只是解析类和方法 身体。幸运的是,这完全满足了我的需求,因为我可以构建 我自己给树打电话 以下代码列出了由其他方法调用的所有方法,仅检查给定目录(和子目录)中的类文件:
import java.io.*;
导入java.util.*;
导入org.objectweb.asm.ClassReader;
公开课考试{
公共静态void main(字符串[]args)引发异常{
File dir=新文件(args[0]);
Map callMap=newhashmap();
List classFiles=newlinkedlist();
findClassFiles(classFiles,目录);
用于(文件类文件:类文件){
InputStream输入=新文件InputStream(类文件);
新建类读取器(输入).accept(新建MyClassVisitor(调用映射),0);
input.close();
}
对于(Map.Entry:callMap.entrySet()){
String方法=entry.getKey();
Set callers=entry.getValue();
if(callers!=null&&!callers.isEmpty()){
系统输出打印LN(方法);
for(字符串调用方:调用方){
System.out.println(“+”调用者);
}
}
}
}
私有静态void findClassFiles(列表,文件目录){
对于(文件:dir.listFiles()){
if(file.isDirectory()){
findClassFiles(列表、文件);
}else if(file.getName().endsWith(“.class”)){
列表。添加(文件);
}
}
}
}
import java.util.*;
导入org.objectweb.asm.MethodVisitor;
导入org.objectweb.asm.commons.EmptyVisitor;
公共类MyClassVisitor扩展了EmptyVisitor{
私有字符串类名;
私有映射调用映射;
公共MyClassVisitor(映射调用映射){
this.callMap=callMap;
}
@凌驾
公共无效访问(int版本、int访问、字符串名称、字符串签名、,
字符串超名,字符串[]接口){
this.className=名称;
}
@凌驾
public方法访问者访问方法(int访问、字符串名称、字符串描述、,
字符串签名,字符串[]异常){
返回新的MyMethodVisitor(className+“+”name,callMap);
}
}
import java.util.*;
导入org.objectweb.asm.commons.EmptyVisitor;
公共类MyMethodVisitor扩展了EmptyVisitor{
私有字符串方法;
私有映射调用映射;
公共MyMethodVisitor(字符串currentMethod,
映射(调用映射){
this.currentMethod=currentMethod;
this.callMap=callMap;
}
@凌驾
public void visitMethodInsn(int操作码、字符串所有者、字符串名称、,
字符串描述){
字符串calledMethod=owner+“+”name;
Set callers=callMap.get(calledMethod);
if(调用方==null){
调用者=新树集();
callMap.put(calledMethod,调用者);
}
add(currentMethod);
}
}
我认为在这种情况下,拥有一个方法主体是不相关的。访问接口方法是因为调用了visitMethod()并返回了一个新的MyMethodVisitor实例,但之后似乎忽略了此MyMethodVisitor。我从磁盘读取类,因为我需要分析一个未运行的应用程序。
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);
}
}
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);
}
}