获取调用者方法 (java.lang.reflect.Method)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4024587/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 04:26:30  来源:igfitidea点击:

Get callers method (java.lang.reflect.Method)

javareflection

提问by dacwe

I would like to get the calling method java.lang.reflect.Method. NOTthe name of the method.

我想得到调用方法java.lang.reflect.Method不是方法的名称。

Here is an example how to get the callers Class.

这是一个如何获取调用者类的示例。

// find the callers class
Thread t = Thread.getCurrentThread();
Class<?> klass = Class.forName(t.getStackTrace()[2].getClassName());
// do something with the class (like processing its annotations)
... 

It's for testing purpose only!

仅供测试使用!

采纳答案by Adam Paynter

If it's just for testing, then this may work. It assumes that the class files are accessible via the calling class's ClassLoaderand that the class files were compiled with debugging symbols (which I hope they are for testing!). This code relies on the ASM bytecode library.

如果只是为了测试,那么这可能有效。它假设类文件可以通过调用类访问ClassLoader,并且类文件是用调试符号编译的(我希望它们用于测试!)。此代码依赖于ASM 字节码库

public static Method getMethod(final StackTraceElement stackTraceElement) throws Exception {
    final String stackTraceClassName = stackTraceElement.getClassName();
    final String stackTraceMethodName = stackTraceElement.getMethodName();
    final int stackTraceLineNumber = stackTraceElement.getLineNumber();
    Class<?> stackTraceClass = Class.forName(stackTraceClassName);

    // I am only using AtomicReference as a container to dump a String into, feel free to ignore it for now
    final AtomicReference<String> methodDescriptorReference = new AtomicReference<String>();

    String classFileResourceName = "/" + stackTraceClassName.replaceAll("\.", "/") + ".class";
    InputStream classFileStream = stackTraceClass.getResourceAsStream(classFileResourceName);

    if (classFileStream == null) {
        throw new RuntimeException("Could not acquire the class file containing for the calling class");
    }

    try {
        ClassReader classReader = new ClassReader(classFileStream);
        classReader.accept(
                new EmptyVisitor() {
                    @Override
                    public MethodVisitor visitMethod(int access, final String name, final String desc, String signature, String[] exceptions) {
                        if (!name.equals(stackTraceMethodName)) {
                            return null;
                        }

                        return new EmptyVisitor() {
                            @Override
                            public void visitLineNumber(int line, Label start) {
                                if (line == stackTraceLineNumber) {
                                    methodDescriptorReference.set(desc);
                                }
                            }
                        };
                    }
                },
                0
            );
    } finally {
        classFileStream.close();
    }

    String methodDescriptor = methodDescriptorReference.get();

    if (methodDescriptor == null) {
        throw new RuntimeException("Could not find line " + stackTraceLineNumber);
    }

    for (Method method : stackTraceClass.getMethods()) {
        if (stackTraceMethodName.equals(method.getName()) && methodDescriptor.equals(Type.getMethodDescriptor(method))) {
            return method;
        }
    }

    throw new RuntimeException("Could not find the calling method");
}

回答by Sean Patrick Floyd

We can almost get there, here's a method that works in many cases. The problem is: it won't work reliably if there are overloaded methods (multiple methods with the same name). The stack trace does not provide the arguments, unfortunately.

我们几乎可以到达那里,这是一种在许多情况下都有效的方法。问题是:如果有重载的方法(多个具有相同名称的方法),它将无法可靠地工作。不幸的是,堆栈跟踪不提供参数。

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

Test code:

测试代码:

public static void main(final String[] args) throws Exception{
    System.out.println(getCallingMethod());
}

Output:

输出:

public static void foo.bar.Phleem.main(java.lang.String[]) throws java.lang.Exception

public static void foo.bar.Phleem.main(java.lang.String[]) 抛出 java.lang.Exception



OK, here is a solution using ASM. It works for almost all cases:

好的,这是使用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();

}

I'll leave it to you to refactor this into something readable. And it won't work if the signature of the calling method contains primitive arrays or multidimensional arrays. Obviously it only works if the class file contains line numbers.

我会让你把它重构为可读的东西。如果调用方法的签名包含原始数组或多维数组,它将不起作用。显然它只有在类文件包含行号时才有效。

Argghh, I work for ages and then I see that someone has come up with an almost identical solution!!! Anyway, I'll leave mine, because I developed it independently.

啊,我工作了很长时间,然后我看到有人提出了几乎相同的解决方案!!!无论如何,我会离开我的,因为我是独立开发的。

回答by Jeff Williams

Here is a modified version of Sean Patrick Floyd's posted method for getting a Java Class from an ASM Type. It fixes a problem with multidimensional arrays and another problem with classes loaded by other classloaders.

这是 Sean Patrick Floyd 发布的用于从 ASM 类型获取 Java 类的方法的修改版本。它修复了多维数组的问题和其他类加载器加载的类的另一个问题。

public static Class<?> getClassFromType(Class<?> clazz, final Type type) throws ClassNotFoundException{
    Class<?> javaType = null;
    switch( type.getSort() ) {
        case Type.VOID      : javaType = Void.TYPE; break;
        case Type.BOOLEAN   : javaType = Boolean.TYPE; break;
        case Type.CHAR      : javaType = Character.TYPE; break;
        case Type.BYTE      : javaType = Byte.TYPE; break;
        case Type.SHORT     : javaType = Short.TYPE; break;
        case Type.INT       : javaType = Integer.TYPE; break;
        case Type.FLOAT     : javaType = Float.TYPE; break;
        case Type.LONG      : javaType = Long.TYPE; break;
        case Type.DOUBLE    : javaType = Double.TYPE; break;
        case Type.ARRAY     : javaType = Array.newInstance( getClassFromType( clazz, type.getElementType()), new int[type.getDimensions()] ).getClass(); break; 
        case Type.OBJECT    : javaType = Class.forName( type.getClassName(), false, clazz.getClassLoader() ); break;
    }
    if ( javaType != null ) return javaType;
    throw new ClassNotFoundException( "Couldn't find class for type " + type );
}

回答by fasseg

quite easy: just get the corresponding Class object first and then use Class.getMethod(String name,params...)

很简单:只需先获取相应的 Class 对象,然后使用 Class.getMethod(String name,params...)

check here for the javadoc

在此处查看 javadoc

public class GetMethod {
    public static void main(String[] args){
        new GetMethod().checkMethod();
    }

    public void checkMethod(){
        Thread t=Thread.currentThread();
        StackTraceElement element=t.getStackTrace()[1];
        System.out.println(element.getClassName());
        System.out.println(element.getMethodName());
        try{
            Method m=Class.forName(element.getClassName()).getMethod(element.getMethodName(),null);
            System.out.println("Method: " + m.getName());
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}

hope that helped....

希望有所帮助....