我需要找到一个方法的调用者.是否可以使用堆栈跟踪或反射?
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()
根据Javadocs:
数组的最后一个元素表示堆栈的底部,这是序列中最近的方法调用.
一StackTraceElement
有getClassName()
,getFileName()
,getLineNumber()
和getMethodName()
.
您将不得不尝试确定您想要的索引(可能stackTraceElements[1]
或[2]
).
可以在对此增强请求的评论中找到替代解决方案.它使用getClassContext()
自定义的方法,SecurityManager
似乎比堆栈跟踪方法更快.
以下程序测试不同建议方法的速度(最有趣的位在内部类中SecurityManagerMethod
):
/** * Test the speed of various methods for getting the caller class name */ public class TestGetCallerClassName { /** * Abstract class for testing different methods of getting the caller class name */ private static abstract class GetCallerClassNameMethod { public abstract String getCallerClassName(int callStackDepth); public abstract String getMethodName(); } /** * Uses the internal Reflection class */ private static class ReflectionMethod extends GetCallerClassNameMethod { public String getCallerClassName(int callStackDepth) { return sun.reflect.Reflection.getCallerClass(callStackDepth).getName(); } public String getMethodName() { return "Reflection"; } } /** * Get a stack trace from the current thread */ private static class ThreadStackTraceMethod extends GetCallerClassNameMethod { public String getCallerClassName(int callStackDepth) { return Thread.currentThread().getStackTrace()[callStackDepth].getClassName(); } public String getMethodName() { return "Current Thread StackTrace"; } } /** * Get a stack trace from a new Throwable */ private static class ThrowableStackTraceMethod extends GetCallerClassNameMethod { public String getCallerClassName(int callStackDepth) { return new Throwable().getStackTrace()[callStackDepth].getClassName(); } public String getMethodName() { return "Throwable StackTrace"; } } /** * Use the SecurityManager.getClassContext() */ private static class SecurityManagerMethod extends GetCallerClassNameMethod { public String getCallerClassName(int callStackDepth) { return mySecurityManager.getCallerClassName(callStackDepth); } public String getMethodName() { return "SecurityManager"; } /** * A custom security manager that exposes the getClassContext() information */ static class MySecurityManager extends SecurityManager { public String getCallerClassName(int callStackDepth) { return getClassContext()[callStackDepth].getName(); } } private final static MySecurityManager mySecurityManager = new MySecurityManager(); } /** * Test all four methods */ public static void main(String[] args) { testMethod(new ReflectionMethod()); testMethod(new ThreadStackTraceMethod()); testMethod(new ThrowableStackTraceMethod()); testMethod(new SecurityManagerMethod()); } private static void testMethod(GetCallerClassNameMethod method) { long startTime = System.nanoTime(); String className = null; for (int i = 0; i < 1000000; i++) { className = method.getCallerClassName(2); } printElapsedTime(method.getMethodName(), startTime); } private static void printElapsedTime(String title, long startTime) { System.out.println(title + ": " + ((double)(System.nanoTime() - startTime))/1000000 + " ms."); } }
运行Java 1.6.0_17的2.4 GHz Intel Core 2 Duo MacBook的输出示例:
Reflection: 10.195 ms. Current Thread StackTrace: 5886.964 ms. Throwable StackTrace: 4700.073 ms. SecurityManager: 1046.804 ms.
内部反射的方法是多比别人快.从新创建的堆栈跟踪获取堆栈跟踪Throwable
比从当前获取堆栈跟踪更快Thread
.在查找调用者类的非内部方式中,自定义SecurityManager
似乎是最快的.
作为lyomi中指出此评论的sun.reflect.Reflection.getCallerClass()
方法已经默认在Java 7中更新40被禁用,完全用Java 8了解更多关于这在去除这个问题在Java bug数据库.
正如zammbi所发现的那样,甲骨文被迫退出了取消该计划的变革sun.reflect.Reflection.getCallerClass()
.它仍然可以在Java 8中使用(但不推荐使用).
3年后:使用当前JVM更新时序.
> java -version java version "1.8.0" Java(TM) SE Runtime Environment (build 1.8.0-b132) Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode) > java TestGetCallerClassName Reflection: 0.194s. Current Thread StackTrace: 3.887s. Throwable StackTrace: 3.173s. SecurityManager: 0.565s.
听起来你正试图避免将引用传递给this
方法.传递this
比通过当前堆栈跟踪查找调用者更好. 重构为更多OO设计甚至更好. 您不应该需要知道呼叫者.如有必要,传递回调对象.
JEP 259为堆栈遍历提供了一种有效的标准API,允许轻松过滤和延迟访问堆栈跟踪中的信息.在Stack-Walking API之前,访问堆栈帧的常用方法是:
Throwable::getStackTrace
并Thread::getStackTrace
返回一个StackTraceElement
对象数组 ,其中包含每个stack-trace元素的类名和方法名.
SecurityManager::getClassContext
是一个受保护的方法,它允许SecurityManager
子类访问类上下文.JDK内部
sun.reflect.Reflection::getCallerClass
方法,你不应该使用它
使用这些API通常效率低下:
这些API要求VM急切地捕获整个堆栈的快照,并返回表示整个堆栈的信息.如果调用者只对堆栈中的前几帧感兴趣,则无法避免检查所有帧的成本.
为了找到直接调用者的类,首先获得一个StackWalker
:
StackWalker walker = StackWalker .getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
然后要么致电getCallerClass()
:
Class> callerClass = walker.getCallerClass();
或walk
在StackFrame
S和获得第1跟前StackFrame
:
walker.walk(frames -> frames .map(StackWalker.StackFrame::getDeclaringClass) .skip(1) .findFirst());
Oneliner:
Thread.currentThread().getStackTrace()[2].getMethodName()
请注意,您可能需要将2替换为1.
这个方法做了同样的事情,但更简单,可能更高效,如果你使用反射,它会自动跳过这些帧.唯一的问题是它可能不存在于非Sun JVM中,尽管它包含在JRockit 1.4的运行时类 - > 1.6中.(重点是,它不是公共课).
sun.reflect.Reflection /** Returns the class of the methodrealFramesToSkip
frames up the stack (zero-based), ignoring frames associated with java.lang.reflect.Method.invoke() and its implementation. The first frame is that associated with this method, sogetCallerClass(0)
returns the Class object for sun.reflect.Reflection. Frames associated with java.lang.reflect.Method.invoke() and its implementation are completely ignored and do not count toward the number of "real" frames skipped. */ public static native Class getCallerClass(int realFramesToSkip);
至于realFramesToSkip
值应该是什么,Sun 1.5和1.6 VM版本java.lang.System
,有一个名为getCallerClass()的包保护方法调用sun.reflect.Reflection.getCallerClass(3)
,但在我的帮助实用程序类中我使用了4,因为有一个辅助类的添加框架调用.
我之前做过这个.您可以创建一个新的异常并在其上抓取堆栈跟踪而不抛出它,然后检查堆栈跟踪.正如另一个答案所说的那样,它的成本非常高 - 不要在紧密的环路中进行.
我之前已经完成了在应用程序上的日志实用程序,其中性能并不重要(性能很少,实际上 - 只要您将结果显示为快速按钮单击等操作).
在您获得堆栈跟踪之前,异常只有.printStackTrace()所以我不得不将System.out重定向到我自己创建的流,然后(new Exception()).printStackTrace(); 重定向System.out并解析流.好玩的东西.
/** * Get the method name for a depth in call stack.
* Utility function * @param depth depth in the call stack (0 means current method, 1 means call method, ...) * @return method name */ public static String getMethodName(final int depth) { final StackTraceElement[] ste = new Throwable().getStackTrace(); //System. out.println(ste[ste.length-depth].getClassName()+"#"+ste[ste.length-depth].getMethodName()); return ste[ste.length - depth].getMethodName(); }
例如,如果您尝试将调用方法行用于调试目的,则需要通过Utility类来编写这些静态方法:(
旧的java1.4代码,仅用于说明潜在的StackTraceElement用法)
/** * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils".
* From the Stack Trace. * @return "[class#method(line)]: " (never empty, first class past StackTraceUtils) */ public static String getClassMethodLine() { return getClassMethodLine(null); } /** * Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils" and aclass.
* Allows to get past a certain class. * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. * @return "[class#method(line)]: " (never empty, because if aclass is not found, returns first class past StackTraceUtils) */ public static String getClassMethodLine(final Class aclass) { final StackTraceElement st = getCallingStackTraceElement(aclass); final String amsg = "[" + st.getClassName() + "#" + st.getMethodName() + "(" + st.getLineNumber() +")] <" + Thread.currentThread().getName() + ">: "; return amsg; } /** * Returns the first stack trace element of the first class not equal to "StackTraceUtils" or "LogUtils" and aClass.
* Stored in array of the callstack.
* Allows to get past a certain class. * @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils. * @return stackTraceElement (never null, because if aClass is not found, returns first class past StackTraceUtils) * @throws AssertionFailedException if resulting statckTrace is null (RuntimeException) */ public static StackTraceElement getCallingStackTraceElement(final Class aclass) { final Throwable t = new Throwable(); final StackTraceElement[] ste = t.getStackTrace(); int index = 1; final int limit = ste.length; StackTraceElement st = ste[index]; String className = st.getClassName(); boolean aclassfound = false; if(aclass == null) { aclassfound = true; } StackTraceElement resst = null; while(index < limit) { if(shouldExamine(className, aclass) == true) { if(resst == null) { resst = st; } if(aclassfound == true) { final StackTraceElement ast = onClassfound(aclass, className, st); if(ast != null) { resst = ast; break; } } else { if(aclass != null && aclass.getName().equals(className) == true) { aclassfound = true; } } } index = index + 1; st = ste[index]; className = st.getClassName(); } if(resst == null) { //Assert.isNotNull(resst, "stack trace should null"); //NO OTHERWISE circular dependencies throw new AssertionFailedException(StackTraceUtils.getClassMethodLine() + " null argument:" + "stack trace should null"); //$NON-NLS-1$ } return resst; } static private boolean shouldExamine(String className, Class aclass) { final boolean res = StackTraceUtils.class.getName().equals(className) == false && (className.endsWith("LogUtils" ) == false || (aclass !=null && aclass.getName().endsWith("LogUtils"))); return res; } static private StackTraceElement onClassfound(Class aclass, String className, StackTraceElement st) { StackTraceElement resst = null; if(aclass != null && aclass.getName().equals(className) == false) { resst = st; } if(aclass == null) { resst = st; } return resst; }