JSP動態(tài)解析過程
請求訪問一個JSP文件的時候,整個過程是這樣的:
JSP文件修改過后,之所以能及時生效,是因為Web容器(Tomcat)會檢查請求的JSP文件是否被更改過,如果發(fā)生過更改,那么就將JSP文件重新解析翻譯成一個新的Sevlet類,并加載到JVM中,之后的請求,都會由這個新的Servet來處理。這里有個問題,根據(jù)Java的類加載機制,在同一個ClassLoader中,類是不允許重復的,為了繞開這個限制,Web容器每次都會創(chuàng)建一個新的ClassLoader實例,來加載新編譯的Servlet類,之后的請求都會由這個新的Servlet來處理,這樣就實現(xiàn)了新舊JSP的切換。
HTTP服務是無狀態(tài)的,所以JSP的場景基本上都是一次性消費,這種通過創(chuàng)建新的ClassLoader來“替換”class的做法行得通,但是對于其他應用,比如Spring框架,即便這樣做了,對象多數(shù)是單例,對于內(nèi)存中已經(jīng)創(chuàng)建好的對象,我們無法通過這種創(chuàng)建新的ClassLoader實例的方法來修改對象行為。
java.lang.instrument.Instrumentation
看完文檔之后,我們發(fā)現(xiàn)這么兩個接口:reDefineClasses和reTransformClasses。一個是重新定義class,一個是修改class。
直接操作字節(jié)碼 ASM、CGLib
BTrace
https://github.com/btraceio/btrace
BTrace是基于Java語言的一個安全的、可提供動態(tài)追蹤服務的工具。BTrace基于ASM、Java Attach Api、Instruments開發(fā),為用戶提供了很多注解。依靠這些注解,我們可以編寫B(tài)Trace腳本(簡單的Java代碼)達到我們想要的效果,而不必深陷于ASM對字節(jié)碼的操作中不可自拔。
package com.sun.btrace.samples;
import com.sun.btrace.annotations.*;
import com.sun.btrace.AnyType;
import static com.sun.btrace.BTraceUtils.*;
/**
* This sample demonstrates regular expression
* probe matching and getting input arguments
* as an array - so that any overload variant
* can be traced in "one place". This example
* traces any "readXX" method on any class in
* java.io package. Probed class, method and arg
* array is printed in the action.
*/
@BTrace public class ArgArray {
@OnMethod(
clazz="/java\\.io\\..*/",
method="/read.*/"
)
public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {
println(pcn);
println(pmn);
printArray(args);
}
}
package com.sun.btrace.samples;
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.Export;
/**
* This sample creates a jvmstat counter and
* increments it everytime Thread.start() is
* called. This thread count may be accessed
* from outside the process. The @Export annotated
* fields are mapped to jvmstat counters. The counter
* name is "btrace." + + "." +
*/
@BTrace public class ThreadCounter {
// create a jvmstat counter using @Export
@Export private static long count;
@OnMethod(
clazz="java.lang.Thread",
method="start"
)
public static void onnewThread(@Self Thread t) {
// updating counter is easy. Just assign to
// the static field!
count++;
}
@OnTimer(2000)
public static void ontimer() {
// we can access counter as "count" as well
// as from jvmstat counter directly.
println(count);
// or equivalently ...
println(Counters.perfLong("btrace.com.sun.btrace.samples.ThreadCounter.count"));
}
}
BTrace的架構是怎樣的呢?
BTrace主要有下面幾個模塊:
BTrace腳本:利用BTrace定義的注解,我們可以很方便地根據(jù)需要進行腳本的開發(fā)。
Compiler:將BTrace腳本編譯成BTrace class文件。
Client:將class文件發(fā)送到Agent。
Agent:基于Java的Attach Api,Agent可以動態(tài)附著到一個運行的JVM上,然后開啟一個BTrace Server,接收client發(fā)過來的BTrace腳本;解析腳本,然后根據(jù)腳本中的規(guī)則找到要修改的類;修改字節(jié)碼后,調(diào)用Java Instrument的reTransform接口,完成對對象行為的修改并使之生效。
Ref: