【问题标题】:Get callers method (java.lang.reflect.Method)获取调用者方法(java.lang.reflect.Method)
【发布时间】:2011-10-07 20:03:37
【问题描述】:

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

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

// 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)
... 

仅供测试!

【问题讨论】:

  • 为什么?这也是为了我个人的满足。
  • 好吧好吧,知道能不能完成是为了个人的满足……

标签: java reflection


【解决方案1】:

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

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

【讨论】:

  • @dacwe:是的,很遗憾该功能没有内置到标准库中。如果 ASM 不可接受,您可以使用任何字节码库。
  • 是的,但我喜欢 ASM,所以没关系。只是以为有人有一个单行标准java解决方案,但这是可以接受的。但是如果你用ASM做,你不能直接在类文件中“注入”某种行->方法信息吗?查找速度更快?
  • 是否还会在每个方法下打印访问指令的行号? @AdamPaynter
【解决方案2】:

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

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

输出:

public static void 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();

}

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

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

【讨论】:

  • @seanizer:哇,这和我的非常相似!作为记录,我的返回实际的Method 而不仅仅是一个字符串表示(尽管我在内部使用了方法的描述符,它本身就是一个字符串)。 ASM 不是很棒吗? :)
  • @seanizer:我们甚至都使用了AtomicReferenceEmptyVisitor
  • 当心上面代码中使用 CHAR_TYPE 的错误 - 它映射到 Float.TYPE 而不是 Character.TYPE。
  • 多维数组也有问题。这将强制使用一维数组类型。我在下面发布了一个工作方式略有不同的更新。
  • @Jeff 是的,这就是我写的原因:如果调用方法的签名包含原始数组或多维数组,它将不起作用
【解决方案3】:

很简单:只要先获取对应的Class对象,再使用Class.getMethod(String name,params...)

check here for the 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();
        }
    }
}

希望对您有所帮助....

【讨论】:

  • 不起作用!如果我们有多个具有相同或参数的方法,它将无法工作。我将在我的问题中添加一个示例(感谢示例!)
  • 好吧,如果该方法不带参数,那它就可以工作。查找当前方法而不是调用方法也相当不值。
【解决方案4】:

这里是 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 );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-08
    • 1970-01-01
    • 1970-01-01
    • 2015-09-16
    • 2019-10-15
    • 2015-12-19
    • 2017-04-19
    • 1970-01-01
    相关资源
    最近更新 更多