Мы можем почти добраться, вот метод, который работает во многих случаях. Проблема в том, что он не будет работать надежно, если есть перегруженные методы (несколько методов с одинаковым именем). К сожалению, трассировка стека не предоставляет аргументов.
private static Method getCallingMethod() throws ClassNotFoundException{
final Thread t = Thread.currentThread();
final StackTraceElement[] stackTrace = t.getStackTrace();
final StackTraceElement ste = stackTrace[2];
final String methodName = ste.getMethodName();
final String className = ste.getClassName();
Class<?> kls = Class.forName(className);
do{
for(final Method candidate : kls.getDeclaredMethods()){
if(candidate.getName().equals(methodName)){
return candidate;
}
}
kls = kls.getSuperclass();
} while(kls != null);
return null;
}
Код теста:
public static void main(final String[] args) throws Exception{
System.out.println(getCallingMethod());
}
Выход:
публичная статическая пустота foo.bar.Phleem.main (java.lang.String []) выбрасывает java.lang.Exception
ОК, вот решение, использующее ASM . Работает практически во всех случаях:
private static Method getCallingMethod() throws ClassNotFoundException,
IOException{
final Thread t = Thread.currentThread();
final StackTraceElement[] stackTrace = t.getStackTrace();
final StackTraceElement ste = stackTrace[2];
final String methodName = ste.getMethodName();
final int lineNumber = ste.getLineNumber();
final String className = ste.getClassName();
final Class<?> kls = Class.forName(className);
final ClassReader cr = new ClassReader(className);
final EmptyVisitor empty = new EmptyVisitor();
final AtomicReference<Method> holder = new AtomicReference<Method>();
cr.accept(new ClassAdapter(empty){
@Override
public MethodVisitor visitMethod(
final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions){
return name.equals(methodName) ? new MethodAdapter(empty){
@Override
public void visitLineNumber(final int line,
final Label start){
if(line >= lineNumber && holder.get() == null){
final Type[] argumentTypes =
Type.getArgumentTypes(desc);
final Class<?>[] argumentClasses =
new Class[argumentTypes.length];
try{
for(int i = 0; i < argumentTypes.length; i++){
final Type type = argumentTypes[i];
final String dd = type.getDescriptor();
argumentClasses[i] = getClassFromType(type);
}
holder.set(kls.getDeclaredMethod(methodName,
argumentClasses));
} catch(final ClassNotFoundException e){
throw new IllegalStateException(e);
} catch(final SecurityException e){
throw new IllegalStateException(e);
} catch(final NoSuchMethodException e){
throw new IllegalStateException(e);
}
}
super.visitLineNumber(line, start);
}
private Class<?> getClassFromType(final Type type) throws ClassNotFoundException{
Class<?> javaType;
final String descriptor = type.getDescriptor();
if(type.equals(Type.INT_TYPE)){
javaType = Integer.TYPE;
} else if(type.equals(Type.LONG_TYPE)){
javaType = Long.TYPE;
} else if(type.equals(Type.DOUBLE_TYPE)){
javaType = Double.TYPE;
} else if(type.equals(Type.FLOAT_TYPE)){
javaType = Float.TYPE;
} else if(type.equals(Type.BOOLEAN_TYPE)){
javaType = Boolean.TYPE;
} else if(type.equals(Type.BYTE_TYPE)){
javaType = Byte.TYPE;
} else if(type.equals(Type.CHAR_TYPE)){
javaType = Character.TYPE;
} else if(type.equals(Type.SHORT_TYPE)){
javaType = Short.TYPE;
} else if(descriptor.startsWith("[")){
final Class<?> elementType =
getClassFromType(type.getElementType());
javaType =
Array.newInstance(elementType, 0).getClass();
} else{
javaType = Class.forName(type.getClassName());
}
return javaType;
}
}
: null;
}
},
0);
return holder.get();
}
Я оставлю это вам, чтобы преобразовать это в нечто читабельное. И это не сработает, если сигнатура вызывающего метода содержит примитивные или многомерные массивы. Очевидно, это работает только в том случае, если файл класса содержит номера строк.
Ааааа, я работаю целую вечность, а потом вижу, что кто-то придумал почти идентичное решение !!! Во всяком случае, я оставлю свой, потому что я разработал его самостоятельно.