How to get a call method with multiple signals with reflection?

Let's pretend that:

@WebserviceUrl("/withObj") public void caller(Object obj){ called(); } @WebserviceUrl("/withoutObj") public void caller(){ called(); } 

as you can see, the caller has two signatures. To get a stack trace, we can use:

 StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); 

But it only returns the name of the method. How can I find the actual true caller?

UPDATE:
The main intention is to read the webservice url that is declared in the method annotation. Incorrect caller method detection, causing an incorrect web service call.

+5
source share
3 answers

Interesting. A possible approach that I could do is do the same as a person would: read the line number on the stack, and then go to the class. It seems that this can be done as follows: How to get the line number of a method? . This is not directly applicable, since CtClass.getDeclaredMethod gives only one method signature. However, you can do something like this:

 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(); 
+2
source

The answer above beat me up, but I would add that you have to go through a few frames of the stack before you get the class or method using the annotations you are looking for, even if the source code is in the same class (but you probably know this) .

For example, here was my class "Test" ...

 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(); }); } } 

The way out of this was ...

 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] 

Here's the StackTraceUtil for completeness. You see, I hardcoded "3" to get the stace trace element from the array. You probably need to go through everything from element 3 until you find your annotation.

(This is not as elegant as the answer above, but as I was almost done, I thought I would send it anyway ...)

 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; } } 

Hope this helps.

+2
source

If your target is reading @WebserviceUrl to do something and call a method, then why not use a proxy server? .. here is an example. I do not understand why you need to read a line of code. It can be changed at any time ... if your goals are reading the webservice url .. maybe this will be useful

Interface for your calling class

 public interface Test { @WebServiceUrl("/withObj") public void caller(Object obj); @WebServiceUrl("/withoutObj") public void caller(); } 

Implantation of this class

 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"); } } 

Call handler class

 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; } } } 

Class proxyfactory

 public class TestProxyFactory { public static Object newInstance(Object ob) { return Proxy.newProxyInstance(ob.getClass().getClassLoader(), new Class<?>[]{Test.class}, new TestProxyHandler(ob)); } } 

And your last main class

  public static void main(String[] args) { Test tester = (Test) TestProxyFactory.newInstance(new TestImpl()); tester.caller(); tester.caller("Test Value"); } 
+1
source

All Articles