You can use bytecode utilities to get the bytecode infomation. And then use the line number info in StackTraceElement
to get the method
.
Here I use javassist.
private static Optional<Method> get(StackTraceElement e) throws NotFoundException, ClassNotFoundException {
Class<?> clz = Class.forName(e.getClassName());
int line = e.getLineNumber();
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(clz.getName());
return Observable.fromArray(cc.getDeclaredMethods())
.sorted(Comparator.comparing(m -> m.getMethodInfo().getLineNumber(0)))
.filter(m -> m.getMethodInfo().getLineNumber(0) <= line)
.map(Optional::of)
.blockingLast(Optional.empty())
.map(m -> uncheck(() -> clz.getDeclaredMethod(m.getName(),
Arrays.stream(m.getParameterTypes()).map(c -> uncheck(() -> nameToClass(c.getName()))).toArray(Class[]::new))));
}
private static Class<?> nameToClass(String name) throws ClassNotFoundException {
switch (name) {
case "int":
return int.class;
case "short":
return short.class;
case "long":
return long.class;
case "double":
return double.class;
case "float":
return float.class;
case "boolean":
return boolean.class;
case "char":
return char.class;
case "byte":
return byte.class;
case "void":
return void.class;
}
if (name.endsWith("[]")) {
return Array.newInstance(nameToClass(name.substring(0, name.length() - 2)), 0).getClass();
}
return Class.forName(name);
}
uncheck
is my utility only ignore the exception in lambda, you can simply use try-catch. see source here
then we test our code
public static void main(String[] args) throws Exception {
// test normal
System.out.println(get(new Exception().getStackTrace()[0]));
// test lambda
Runnable r = () -> System.out.println(uncheck(() -> get(new Exception().getStackTrace()[0])));
r.run();
// test function
testHere(1);
}
private static void testHere(int i) throws Exception {
System.out.println(get(new Exception().getStackTrace()[0]));
}
and output
Optional[public static void xdean.stackoverflow.java.reflection.Q44563354.main(java.lang.String[]) throws java.lang.Exception]
Optional[private static java.util.Optional xdean.stackoverflow.java.reflection.Q44563354.lambda$1() throws java.lang.Exception]
Optional[private static void xdean.stackoverflow.java.reflection.Q44563354.testHere(int) throws java.lang.Exception]
But note that this approach only works for class has bytecode in classpath. If you use dynamic proxy, it doesn't work.
Find complete sample code here
EDIT: You can filter lambda method to get the actual method.