如何使用反射获得多签名调用方法?

时间:2015-07-25 11:18:57

标签: java reflection

假设我们有:

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

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

如您所见,来电者有两个签名。为了获得堆栈跟踪,我们可以使用:

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

但它只返回方法的名称。我怎样才能找到真正的真实来电者?

更新:
该问题的主要目的是阅读在方法注释中声明的webservice url。错误检测到调用方法,导致调用错误的Web服务。

3 个答案:

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