どうやって方法を知っていますか?



http://hellboys.bokee.com/1904804.htmlから回転
例えば、2つのクラスがあります。クラスA、クラスB
Class Aの一例は、Class Bの方法を呼び出して、動的エージェントを通じてこの呼び出しを傍受することができますが、Class Bを呼び出したのは誰ですか?どうやって切り取りますか?
考えを下にあげます
javaコード:
public static String getCaller(){ 
    int i; 
    StackTraceElement stack[] = (new Throwable()).getStackTrace(); 
    for (i=0; i < stack.length; i++) { 
      StackTraceElement ste=stack; 
      System.out.println(ste.getClassName()+"."+ste.getMethodName()+"(...)"); 
      System.out.println(i+"--"+ste.getMethodName()); 
      System.out.println(i+"--"+ste.getFileName()); 
      System.out.println(i+"--"+ste.getLineNumber()); 
    } 
  }
 
スレッド運転スタック情報の取得
一、問題の導入
Javaプログラムでログ機能(JDK LogまたはLog 4 J)を使うと、ログシステムが自動的に豊富な情報を印刷してくれることが分かります。フォーマットは以下の通りです。
[運転時間][現在クラス名][メソッド名]
INFO:[ユーザ情報]
具体例はTomcat起動情報のようです。
Jull 9,2004 11:22:41 AM org.apache.com yote.http 11 Prottocol start
INFO:Starting Coyote HTTP/1.1 on port 8080
この不思議なところがないように見えますが、情報を印刷したのではないですか?しかし好奇心が強いと、後の原理を追えば、それは本当に不思議です。
上のログ情報の「現在のクラス名」「メソッド名」部分はユーザが自分で追加したものではなく、ログシステムが自動的に追加されたものです。これは、ログシステムが、現在実行されているステートメントがどのような方法かを自動的に判断できるということです。これはどうやってできますか?
私たちはjava.lang.reflection packageをめくり、ステージステートメントレベルのReflectionクラスを見つけて、このStationオブジェクトを通じてMethodを獲得し、このMethodを通じてdeclead Classを獲得することを夢見ています。これは対応するクラスとMethodの情報を得たのではないですか?これはいい構想だが、一つの構想しかない。このステージの対象がないからです。
もう少し考えてみます。そうだ、JavaにはThread類があるじゃないですか?Thread.current Thread()の方法は現在のスレッドを取得します。この現在のスレッドを通じて現在実行中のMethodとクラスを取得できますか?残念ですが、JDK 1.4または以下のバージョンを使っていると、このような方法は見つけられません。(JDK 1.5の場合は後で言います)
もう少し考えてみます。はい、私達はすべてとても深い印象があって、システムがExceptionを投げる時、いつも一連の情報を印刷して、私達にExceptionの発生の位置を教えて、1階の階の呼び出しと関係します。私たちは、ExceptionのprintStockTrace()方法を自分で呼び出してこれらの情報を印刷することもできます。これは現在のスレッドの運行スタックの情報ではないですか?見つけました。それです。
Exceptionのprint StockTrace()方法はThrowableから継承されていますが、JDKのThrowableのprint StockTrace()方法はどのように実現されていますか?
まずJDK 1.3のソースコードを見てみますと、Throwable.print StockTrace()方法は一つのnative print Stock Trace 0()方法を呼び出しています。私たちは何の手がかりも見つけられません。私たち自身のJavaコードに使えます。
どうすればいいですか?Throwable.print StockTraceの出力結果文字列には、現在のスレッド運転スタックのすべての情報が含まれているのではないですか?この文字列から必要な情報を抽出することができます。JDK 1.3の時代も、そうするしかなかった。
二、Log 4 J 1.2の関連実現
Log 4 J 1.2はJDK 1.3時代の作品です。関連ソースコードを見に来ました。
javaコード:
/** 
Instantiate location information based on a Throwable. We 
expect the Throwable t, to be in the format 
 
java.lang.Throwable 
... 
at org.apache.log4j.PatternLayout.format(PatternLayout.java:413) 
at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183) 
at org.apache.log4j.Category.callAppenders(Category.java:131) 
at org.apache.log4j.Category.log(Category.java:512) 
at callers.fully.qualified.className.methodName(FileName.java:74) 
...
*/ 
public LocationInfo(Throwable t, String fqnOfCallingClass) { 
String s; 
… 
t.printStackTrace(pw); 
s = sw.toString(); 
sw.getBuffer().setLength(0); 
…. //         
}
 
 
ここでは全体的な実現構想が見られます。
まず、t.print StockTrace(pw);stack trace文字列を取得します。このtはnew Throwableの結果です。ユーザープログラムがLog 4 Jメソッドを呼び出した後、Log 4 Jは自分でまた4回の呼び出しを行い、t=new Throwable():
at org.apaache.log 4 j.Pattern Layout.format(Pattern Layout.java:413)
at org.apache.log 4 j.FileApppender.doApppend(FileApppender.java:183)
at org.apache.log 4 j.category.calApenders(Category.java:131)
at org.apache.log 4 j.category.java:512)
その後、4行進むと、ユーザプログラム自体の呼び出し情報に戻ることができます。
at calles.fully.qualifed.className.methodName(FileName.java:74)
この行には、クラス名、メソッド名、ファイル名、行番号などの情報が全部あります。この行を解析すれば、必要なすべての情報が得られます。
三、JDK 1.4 Logの関連実現
Log 4 Jは大成功を収め,SunはJDK 1.4にこのLog機能を導入することを決定した。
StockTrace文字列のトラブルを解決するために、JDK 1.4は新しいクラスを導入しました。StockTraceElement。
public final class StackTraceElement implements java.io.Serializable { 
// Normally initialized by VM (public constructor added in 1.5) 
private String declaringClass; 
private String methodName; 
private String fileName; 
private int lineNumber;
 
クラス名、メソッド名、ファイル名、行番号などが含まれています。
JDK 1.4 Logに関する実現を見に来ました。
LocationInfo.javaのinfo Caller方法(推計調合者)
// Private method to infer the caller's class and method names 
private void inferCaller() { 
… 
// Get the stack trace. 
StackTraceElement stack[] = (new Throwable()).getStackTrace(); 
// First, search back to a method in the Logger class. 
…. //         
// Now search for the first frame before the "Logger" class. 
while (ix < stack.length) { 
StackTraceElement frame = stack[ix]; 
String cname = frame.getClassName(); 
if (!cname.equals("java.util.logging.Logger")) 
// We've found the relevant frame. 
… //         
} 
// We haven't found a suitable frame, so just punt. This is 
// OK as we are only committed to making a "best effort" here. 
}
 
注釈から考え方がわかる。過程はLog 4 Jと同じです。解析文字列のトラブルは免除されました。
四、Log 4 J 1.3 alphaの関連実現
JDK 1.4にStockTraceElement類が導入された以上、Log 4 Jも時代と共に進化していきます。LocationInfo類もそれなりに変化しています。
/** 
Instantiate location information based on a Throwable. We 
expect the Throwable t, to be in the format
 
java.lang.Throwable 
... 
at org.apache.log4j.PatternLayout.format(PatternLayout.java:413) 
at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183) 
at org.apache.log4j.Category.callAppenders(Category.java:131) 
at org.apache.log4j.Category.log(Category.java:512) 
at callers.fully.qualified.className.methodName(FileName.java:74) 
...
 
However, we can also deal with JIT compilers that "lose" the 
location information, especially between the parentheses.
*/ 
public LocationInfo(Throwable t, String fqnOfInvokingClass) { 
if(PlatformInfo.hasStackTraceElement()) { 
StackTraceElementExtractor.extract(this, t, fqnOfInvokingClass); 
} else { 
LegacyExtractor.extract(this, t, fqnOfInvokingClass); 
} 
}
 
Log 4 JはまずJavaプラットフォームがStocTraceElementをサポートしているかどうかを判断します。
StockTrace Element Extractor.javaに来てください。
/** 
* A faster extractor based on StackTraceElements introduced in JDK 1.4. 
* 
* The present code uses reflection. Thus, it should compile on all platforms. 
* 
* @author Martin Schulz 
* @author Ceki G&lc& 
* 
*/ 
public class StackTraceElementExtractor { 
protected static boolean haveStackTraceElement = false; 
private static Method getStackTrace = null; 
private static Method getClassName = null; 
private static Method getFileName = null; 
private static Method getMethodName = null; 
private static Method getLineNumber = null; 
…. //       
 
Log 4 J 1.3は依然としてJDK 1.3に対応しており、JDK 1.4のためにも最適化されていることが見られます。
五、JDK 1.5のThread Stock Trace
JDK 1.5はThread類にget StocTrace()とget All StockTraces()の2つの方法を導入しました。これで、私たちは使いません。呼び出し可能
Thread.get CurrenntThread().get StockTraceは、現在のスレッドのランスタック情報を取得する。それだけでなく、権限が許可されていれば、他のスレッドのランスタック情報も得られます。
/** 
* Returns an array of stack trace elements representing the stack dump 
* of this thread. This method will return a zero-length array if 
* this thread has not started or has terminated. 
* If the returned array is of non-zero length then the first element of 
* the array represents the top of the stack, which is the most recent 
* method invocation in the sequence. The last element of the array 
* represents the bottom of the stack, which is the least recent method 
* invocation in the sequence. 
* 
*
If there is a security manager, and this thread is not 
* the current thread, then the security manager's 
* checkPermission method is called with a 
* RuntimePermission("getStackTrace") permission 
* to see if it's ok to get the stack trace. 
* 
*
Some virtual machines may, under some circumstances, omit one 
* or more stack frames from the stack trace. In the extreme case, 
* a virtual machine that has no stack trace information concerning 
* this thread is permitted to return a zero-length array from this 
* method. 
* 
* @return an array of StackTraceElement, 
* each represents one stack frame. 
* 
* @since 1.5 
*/ 
public StackTraceElement[] getStackTrace() { 
if (this != Thread.currentThread()) { 
// check for getStackTrace permission 
SecurityManager security = System.getSecurityManager(); 
if (security != null) { 
security.checkPermission( 
SecurityConstants.GET_STACK_TRACE_PERMISSION); 
} 
}
if (!isAlive()) { 
return EMPTY_STACK_TRACE; 
}
Thread[] threads = new Thread[1]; 
threads[0] = this; 
StackTraceElement[][] result = dumpThreads(threads); 
return result[0]; 
}
/** 
* Returns a map of stack traces for all live threads. 
* 
* @since 1.5 
*/ 
public static Map getAllStackTraces() { 
// check for getStackTrace permission 
// Get a snapshot of the list of all threads 
}
 
六、まとめ
全体の発展傾向から見れば、JDKはますます多く、より強い機能を提供するだけでなく、ユーザーに暴露する制御方法もますます多くなり、ますます強くなります。
 
クラス名を直接取得する方法について。
私たちはsun.reflect.Reflectのget Caller Classの方法の説明を見に来ました。
javaコード:
/** Returns the class of the method realFramesToSkip 
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, so 
getCallerClass(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);
 
これはnative方法です。原理は、StockFrame(運営スタック)によって、該当するクラスの情報を取得することである。この方法は直接にクラス名を返します。直接有効です。パラメータrealFraamestoSkipはあなたが必要とするStockレベルを選択するために使用されます。だから、この方法で任意のレベルの上の呼び出しクラス名を得ることができます。