如何获取一个方法的行号?

21
3个回答

20

我希望做同样的事情,在一些研究后选择了javassist。您需要添加javassist库(我使用的是版本3.15.0-GA)。

给定以下类,确定“x”方法的位置。“x”方法名称是硬编码的,但是如果您和我一样,反射并不难,因此我有信心您可以获取方法名称列表,然后以下内容将让您获取方法的行号:

public class Widget {
    void x(){System.out.println("I'm x\n");}
    //comment added to create space
    void y(){System.out.println("I'm y\n");} 
}

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

public class App {
    public static void main(String[] args) throws NotFoundException {
        System.out.println("Get method line number with javassist\n");
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.quaternion.demo.Widget");
        CtMethod methodX = cc.getDeclaredMethod("x");
        int xlineNumber = methodX.getMethodInfo().getLineNumber(0);
        System.out.println("method x is on line " + xlineNumber + "\n");
    }
}

输出: 方法x在第12行,在我的情况下这是精确的。我删掉了一些注释...

: 如Pete83在评论中提到的那样,这个方法实际上返回的是该方法中的第一行代码而不是声明该方法的行。这通常不会成为问题,因为大多数人可能想要建立相对位置(它们被声明的顺序)并使用此信息进行自己的约定。每当您感到有必要在注释中包含序数值时,并且可以通过代码本身的位置轻松确定该值,就会出现这种情况。


对于javassist的快速参考Maven坐标:

<dependency>
   <groupId>org.javassist</groupId> <!-- if a version prior to 3.13.0-GA is needed use "javassist" and not "org.javassist" -->
   <artifactId>javassist</artifactId>
   <version>3.15.0-GA</version>
</dependency>

1
在我的情况下,我想按照实体对象中呈现的顺序输出ORM实体信息。在表格中显示实体时,它们在实体中出现的顺序似乎是一个很好的顺序。为此,我找到了所有的getter并将它们加载到TreeMap<Integer,String>中,其中整数是行号,字符串是getter(实际上由于我将使用EL提取值,所以我将getter处理成属性名称,即去除“get”并将第一个字符小写)。因为我希望这个过程快速... - Quaternion
2
值得一提的是,我遇到了一个 javassist.NotFoundException 的问题,通过添加以下代码解决了该问题:ClassClassPath ccpath = new ClassClassPath(aJavaPipeline); pool.insertClassPath(ccpath); - okrunner
我认为这并没有真正打印出方法的行号,而是方法内的第一行?这也是有道理的,因为方法声明的行号并不真正包含在字节码中,所以最好的猜测只能是取方法内的第一行号并减去1,如果第一行是注释,那当然是错误的...尝试 void foo()\n {\n // 有趣的注释 \nSystem.out.println("foo");\n} 在我的情况下,上述解决方案即使没有注释也不会打印出声明的行号。 - pete83
@peate83,你说得对,我已经测试过了,它实际上指向方法中的第一行,但是对于我所能想到的所有用例(在运行时使用方法顺序来使用约定,也许确定执行顺序或显示顺序),需要的是方法的相对顺序而不是它们的绝对值。我能想到需要真实值的唯一原因是出于某种工具目的...在这种情况下,我想你可能会考虑使用某种词法分析器的构建插件将所需数据存储在文件中。 - Quaternion
1
@YachithaSandaruwan 我认为这是指方法的偏移量,0 指的是方法的第一行... 如果您指定了一个超出范围的索引,它会返回 -1。详情请参见:http://www.javassist.org/html/javassist/bytecode/MethodInfo.html#getLineNumber(int) - Quaternion
显示剩余7条评论

10

对我来说,Quaternion指向Javassist的答案是这个问题的完美解决方案。它对我起作用,当你只获得java.lang.reflect.Method对象时,你也可以使用它。

这是我是如何做到的:

Method m; // the method object
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(m.getDeclaringClass().getCanonicalName());
CtMethod javassistMethod = cc.getDeclaredMethod(m.getName());
int linenumber = javassistMethod.getMethodInfo().getLineNumber(0);

1
很高兴听到这个消息!我很惊讶为什么没有更多的人想要这种功能。顺便说一下,我的用例是在自动生成的表格上创建智能订单。你能分享一下你的用例吗? - Quaternion
当然可以。我的用例是使用AspectJ Reflection API从一个方面获取所有建议。在每个建议中都有一个方法对象。我使用方法对象的行号来按照它们在方面源代码中出现的顺序对建议进行排序。建议的顺序对于调用非常重要。 - Thorben

0

你无法从一个方法中获取行号,因为你不知道这个方法适用于哪个方法。一个方法可以调用你选择的任何子类。


但是如果我也得到了实例呢?我可以从中获取类对象。在类文件的某个地方有一些调试信息/行号。因此从技术上讲,应该可以以某种方式实现。 - Chriss
1
在这种情况下,您可以使用像ASM、BCEL、Javassist这样的库读取字节码,并提取字节码中的第一行。 - Peter Lawrey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接