Как получить класс вызывающего абонента в Java
Я хочу получить класс вызывающего метода, т.е.
class foo{
bar();
}
В панели методов мне нужно получить имя класса foo
, и я нашел этот метод:
Class clazz = sun.reflect.Reflection.getCallerClass(1);
Однако, хотя getCallerClass
- public
, когда я пытаюсь его назвать, Eclipse говорит:
Ограничение доступа: метод getCallerClass() из типа Отражение недоступно из-за ограничений на требуемую библиотеку C:\Program Files\Java\jre7\lib\rt.jar
Есть ли другие варианты?
Ответы
Ответ 1
Вы можете создать трассировку стека и использовать информацию в StackTraceElements.
Например, класс утилиты может вернуть вам имя вызывающего класса:
public class KDebug {
public static String getCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
for (int i=1; i<stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(KDebug.class.getName()) && ste.getClassName().indexOf("java.lang.Thread")!=0) {
return ste.getClassName();
}
}
return null;
}
}
Если вы вызываете KDebug.getCallerClassName()
из bar()
, вы получите "foo"
.
Теперь предположим, что вы хотите знать класс метода, вызывающий bar
(что более интересно и, возможно, то, что вы действительно хотели). Вы можете использовать этот метод:
public static String getCallerCallerClassName() {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
String callerClassName = null;
for (int i=1; i<stElements.length; i++) {
StackTraceElement ste = stElements[i];
if (!ste.getClassName().equals(KDebug.class.getName())&& ste.getClassName().indexOf("java.lang.Thread")!=0) {
if (callerClassName==null) {
callerClassName = ste.getClassName();
} else if (!callerClassName.equals(ste.getClassName())) {
return ste.getClassName();
}
}
}
return null;
}
Это для отладки? Если нет, может быть лучшее решение вашей проблемы.
Ответ 2
Чтобы получить имя класса вызывающего/вызываемого абонента, используйте код ниже, он отлично работает для меня.
String callerClassName = new Exception().getStackTrace()[1].getClassName();
String calleeClassName = new Exception().getStackTrace()[0].getClassName();
Ответ 3
StackTrace
Это сильно зависит от того, что вы ищете... Но это должно получить класс и метод, который вызвал этот метод непосредственно в этом объекте.
- индекс 0 = тема
- индекс 1 = это
- индекс 2 = прямой абонент, может быть самостоятельно.
- index 3... n = классы и методы, которые вызывали друг друга для получения индекса 2 и ниже.
Для класса/метода/имени файла:
Thread.currentThread().getStackTrace()[2].getClassName();
Thread.currentThread().getStackTrace()[2].getMethodName();
Thread.currentThread().getStackTrace()[2].getFileName();
Для класса:
Class.forName(Thread.currentThread().getStackTrace()[2].getClassName())
FYI: Class.forName() генерирует исключение ClassNotFoundException, которое НЕ является средой выполнения. Вам нужно попробовать поймать.
Кроме того, если вы хотите игнорировать вызовы внутри самого класса, вы должны добавить цикл с логикой, чтобы проверить эту конкретную вещь.
Что-то вроде... (я не проверял этот кусок кода, так что будьте осторожны)
StackTraceElement[] stes = Thread.currentThread().getStackTrace();
for(int i=2;i<stes.length;i++)
if(!stes[i].getClassName().equals(this.getClass().getName()))
return stes[i].getClassName();
StackWalker
StackWalker StackFrame
Обратите внимание, что это не обширное руководство, а пример возможности.
Печатает класс каждого StackFrame (захватывая ссылку на класс)
StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
.forEach(frame -> System.out.println(frame.getDeclaringClass()));
Делает то же самое, но сначала собирает поток в список.
Просто для демонстрационных целей.
StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
.walk(stream -> stream.collect(Collectors.toList()))
.forEach(frame -> System.out.println(frame.getDeclaringClass()));
Ответ 4
SecurityManager
имеет защищенный метод getClassContext
Создав класс утилиты, который расширяет SecurityManager, вы можете получить к нему доступ.
public class CallingClass extends SecurityManager {
public static final CallingClass INSTANCE = new CallingClass();
public Class[] getCallingClasses() {
return getClassContext();
}
}
Используйте CallingClass.INSTANCE.getCallingClasses()
для извлечения вызывающих классов.
Существует также небольшая библиотека (отказ от ответственности: мой) WhoCalled, которая раскрывает эту информацию. Он использует Reflection.getCallerClass, когда он доступен, а затем возвращается к SecurityManager.
Ответ 5
Я знаю, что это старый вопрос, но я полагал, что искателю нужен класс, а не имя класса. Я написал небольшой метод, который получит фактический класс. Это своего рода обманщик и может не всегда работать, но иногда, когда вам нужен фактический класс, вам придется использовать этот метод...
/**
* Get the caller class.
* @param level The level of the caller class.
* For example: If you are calling this class inside a method and you want to get the caller class of that method,
* you would use level 2. If you want the caller of that class, you would use level 3.
*
* Usually level 2 is the one you want.
* @return The caller class.
* @throws ClassNotFoundException We failed to find the caller class.
*/
public static Class getCallerClass(int level) throws ClassNotFoundException {
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
String rawFQN = stElements[level+1].toString().split("\\(")[0];
return Class.forName(rawFQN.substring(0, rawFQN.lastIndexOf('.')));
}
Ответ 6
Это самый эффективный способ получить только класс вызывающих. Другие подходы берут полный дамп стека и дают вам только имя класса.
Однако этот класс находится под sun.*
, который действительно используется для внутреннего использования. Это означает, что он может не работать на других платформах Java или даже в других версиях Java. Вы должны решить, является ли это проблемой или нет.
Ответ 7
Найдите ниже простой пример, иллюстрирующий, как получить имена классов и методов.
public static void main(String args[])
{
callMe();
}
void callMe()
{
try
{
throw new Exception("Who called me?");
}
catch( Exception e )
{
System.out.println( "I was called by " +
e.getStackTrace()[1].getClassName() +
"." +
e.getStackTrace()[1].getMethodName() +
"()!" );
}
}
e имеет getClassName()
, getFileName()
, getLineNumber()
и getMethodName()
...
Ответ 8
Сообщение об ошибке, с которым сталкивается OP, является просто функцией Eclipse. Если вы хотите связать код с конкретным производителем (и даже версией) JVM, вы можете эффективно использовать метод sun.reflect.Reflection.getCallerClass()
. Затем вы можете скомпилировать код вне Eclipse или настроить его, чтобы не считать эту диагностическую ошибку.
Худшая конфигурация Eclipse заключается в том, чтобы отключить все ошибки в:
Project Properties
/Java Compiler
/Errors/Warnings
/Enable project specific settings
установить на отметку / Deprecated and restrited API
/Forbidden reference (access rules)
на Warning
или Ignore
.
Лучшая конфигурация Eclipse - отключить определенное появление ошибки:
Project Properties
/Java Build Path
/Libraries
/JRE System Library
expand/ Access rules:
выбрать /Edit...
/Add...
/Resolution:
установить в Discouraged
или Accessible
/Rule Pattern
установить до sun/reflect/Reflection
.
Ответ 9
Я использую следующий метод, чтобы получить вызывающий объект для определенного класса из stacktrace:
package test.log;
public class CallerClassTest {
public static void main(final String[] args) {
final Caller caller = new Caller(new Callee());
caller.execute();
}
private static class Caller {
private final Callee c;
public Caller(final Callee c) {
this.c = c;
}
void execute() {
c.call();
}
}
static class Callee {
void call() {
System.out.println(getCallerClassName(this.getClass()));
}
}
/**
* Searches the current threads stacktrace for the class that called the given class. Returns {@code null} if the
* calling class could not be found.
*
* @param clazz
* the class that has been called
*
* @return the caller that called the class or {@code null}
*/
public static String getCallerClassName(final Class<?> clazz) {
final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
final String className = clazz.getName();
boolean classFound = false;
for (int i = 1; i < stackTrace.length; i++) {
final StackTraceElement element = stackTrace[i];
final String callerClassName = element.getClassName();
// check if class name is the requested class
if (callerClassName.equals(className)) classFound = true;
else if (classFound) return callerClassName;
}
return null;
}
}
Ответ 10
Поскольку в настоящее время у меня такая же проблема, вот что я делаю:
-
Я предпочитаю com.sun.Reflection вместо stackTrace, поскольку трассировка стека создает имя не самого класса (включая самого загрузчика).
-
Метод устарел, но все еще находится в Java 8 SDK.
//Дескриптор метода № 124 (I) Ljava/lang/Class; (Устаревшее) // Подпись: (I) Ljava/lang/Class < * > ; @java.lang.Deprecated public static native java.lang.Class getCallerClass (int arg0);
- Метод без аргумента int не устарел
//дескриптор метода # 122() Ljava/lang/Class; // Подпись:() Ljava/lang/Class < * > ; @sun.reflect.CallerSensitive public static native java.lang.Class getCallerClass();
Поскольку я должен быть независимым от платформы bla bla, включая ограничения безопасности, я просто создаю гибкий метод:
-
Проверьте, доступно ли com.sun.Reflection(исключения безопасности запрещают этот механизм)
-
Если 1 да, то получите метод с int или no int аргументом.
-
Если 2 да, вызовите его.
Если 3. не было достигнуто, я использую трассировку стека, чтобы вернуть имя. Я использую специальный объект результата, который содержит либо класс, либо строку, и этот объект точно сообщает, что это такое и почему.
[Резюме]
Я использую stacktrace для резервного копирования и обходит предупреждения компилятора eclipse. Я использую отражения. Работает очень хорошо. Сохраняет код в чистоте, работает как шарм, а также указывает на корректные проблемы.
Я использую это довольно долгое время, и сегодня я искал связанный с ним вопрос, поэтому