假设我们有:
@WebserviceUrl("/withObj")
public void caller(Object obj){
called();
}
@WebserviceUrl("/withoutObj")
public void caller(){
called();
}
如您所见,来电者有两个签名。为了获得堆栈跟踪,我们可以使用:
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
但它只返回方法的名称。我怎样才能找到真正的真实来电者?
更新:
该问题的主要目的是阅读在方法注释中声明的webservice url。错误检测到调用方法,导致调用错误的Web服务。
答案 0 :(得分:2)
有趣。我想到的一种可能的方法是做与人类相同的方法:读取堆栈跟踪中的行号,然后转到类。它似乎是可行的:How to get the line number of a method?。这不是直接适用的,因为CtClass.getDeclaredMethod
只为您提供了该方法的一个签名。但是,你可以这样做:
String className;
String methodName;
int lineNumber;
// parse the stacktrace to get the name of the class, the name of the method and its line number
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(className);
CtMethod methodWhereExceptionOccurred =
Stream.of(cc.getDeclaredMethods())
.filter(method -> method.getName().equals(methodName))
.filter(method -> method.getMethodInfo().getLineNumber(0) == lineNumber)
.findFirst()
.get();
答案 1 :(得分:2)
上面的回答击败了我,但我补充说,在你获得带有你正在寻找的注释的类或方法之前,你可能需要经历几个堆栈帧,即使源代码在同一个类中(但你可能知道这一点。)
例如,这是我的“测试”课......
import javassist.CtMethod;
public class Test {
private void method(String s) {
called();
}
private void method() {
called();
}
private static void called() {
CtMethod caller = StackTraceUtil.getCaller();
System.out.println(caller);
}
private interface Widgit {
void call();
}
private static void call(Widgit w) {
w.call();
}
public static void main(String[] args) {
new Test().method();
new Test().method("[]");
new Widgit() {
@Override
public void call() {
called();
}
}.call();
call(() -> {
called();
});
}
}
这个输出是......
javassist.CtMethod@e59521a2[private method ()V]
javassist.CtMethod@abb88b98[private method (Ljava/lang/String;)V]
javassist.CtMethod@bbd779f1[static access$0 ()V]
javassist.CtMethod@67f92ed4[private static lambda$0 ()V]
这是StackTraceUtil
的完整性。你看到我硬编码“3”来从阵列中获取stace trace元素。您可能需要遍历从元素3开始的所有内容,直到找到注释。
(这不像上面的答案那么优雅,但是当我差不多完成它时我还以为我会发布它...)
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
public class StackTraceUtil {
private static Map<Class<?>, SortedMap<Integer, CtMethod>> cache = new HashMap<>();
public static CtMethod getCaller() {
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement stackTraceElement = stacktrace[3];
int lineNumber = stackTraceElement.getLineNumber();
try {
return findMethod(Class.forName(stackTraceElement.getClassName()), lineNumber);
} catch (ClassNotFoundException e) {
return null;
}
}
public static CtMethod findMethod(Class<?> clazz, int lineNumber) {
SortedMap<Integer, CtMethod> classInfo = cache.get(clazz);
if (classInfo == null) {
classInfo = populateClass(clazz);
cache.put(clazz, classInfo);
}
if(classInfo != null) {
SortedMap<Integer, CtMethod> map = classInfo.tailMap(lineNumber);
if(!map.isEmpty()) {
return map.values().iterator().next();
}
}
return null;
}
private static SortedMap<Integer, CtMethod> populateClass(Class<?> clazz) {
SortedMap<Integer, CtMethod> result;
try {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(clazz.getCanonicalName());
CtMethod[] methods = cc.getDeclaredMethods();
result = new TreeMap<>();
for (CtMethod ctMethod : methods) {
result.put(ctMethod.getMethodInfo().getLineNumber(0), ctMethod);
}
} catch (NotFoundException ex) {
result = null;
}
return result;
}
}
希望这有帮助。
答案 2 :(得分:1)
如果您的目标是阅读@WebserviceUrl做某事并调用方法那么为什么不使用代理? ..这里是一个例子..我不明白为什么你需要阅读代码行..它可以随时更改...如果你的目标是阅读webservice网址...也许这会有所帮助
来电者班级的接口
public interface Test {
@WebServiceUrl("/withObj")
public void caller(Object obj);
@WebServiceUrl("/withoutObj")
public void caller();
}
实施该类
public class TestImpl implements Test {
@Override
public void caller(Object obj) {
System.out.println("Object with Called");
}
@Override
public void caller() {
System.out.println("Object without Called");
}
}
调用处理程序类
public class TestProxyHandler implements InvocationHandler {
private final Object obj;
public TestProxyHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
final WebServiceUrl annotation = method.getAnnotation(WebServiceUrl.class);
if (annotation != null) {
System.out.println("Value Of annotation :" + annotation.value());
//Do What you want ;
}
return method.invoke(obj, args);
} catch (InvocationTargetException | IllegalAccessException | IllegalArgumentException e) {
throw e;
}
}
}
ProxyFactory Class
public class TestProxyFactory {
public static Object newInstance(Object ob) {
return Proxy.newProxyInstance(ob.getClass().getClassLoader(),
new Class<?>[]{Test.class}, new TestProxyHandler(ob));
}
}
最后一个主要课程
public static void main(String[] args) {
Test tester = (Test) TestProxyFactory.newInstance(new TestImpl());
tester.caller();
tester.caller("Test Value");
}