Tải bản đầy đủ (.pdf) (20 trang)

Động lực học lập trình Java, Phần 5: Việc chuyển đổi các lớp đang hoạt động pdf

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (204.99 KB, 20 trang )

Động lực học lập trình Java, Phần 5: Việc chuyển đổi các lớp đang hoạt động
Tìm hiểu cách thay đổi các lớp khi chúng đang được nạp bằng Javassist
Dennis Sosnoski, Nhà tư vấn, Sosnoski Software Solutions, Inc.
Tóm tắt: Sau thời gian gián đoạn ngắn, Dennis Sosnoski trở lại với phần 5 của
loạt bài Động lực học lập trình Java của mình. Bạn đã thấy cách viết một chương
trình chuyển đổi các tệp lớp Java để thay đổi hành vi mã. Trong bài báo này,
Dennis cho bạn thấy cách kết hợp chuyển đổi với việc nạp các lớp thực sự bằng
cách sử dụng khung công tác Javassist, để xử lý tính năng hướng khía cạnh "đúng
thời gian" linh hoạt. Cách tiếp cận này cho phép bạn quyết định những gì bạn
muốn thay đổi trong thời gian chạy và có khả năng thực hiện các thay đổi khác
nhau mỗi khi bạn chạy một chương trình. Theo cách này, bạn cũng sẽ xem xét sâu
hơn vào các vấn đề chung của việc nạp lớp (classloading) trong JVM.
Trong Phần 4, "Các phép biến đổi lớp bằng Javassist," bạn đã học được cách sử
dụng khung công tác Javassist để chuyển đổi các tệp lớp Java do trình biên dịch
tạo ra, viết lại các tệp lớp đã sửa đổi. Bước chuyển đổi tệp lớp này rất quan trọng
để thực hiện các thay đổi liên tục, nhưng không nhất thiết phải tiện lợi khi bạn
muốn thực hiện các thay đổi khác nhau mỗi khi bạn thực hiện ứng dụng của bạn.
Đối với các thay đổi thoáng qua như vậy, một cách tiếp cận hoạt động khi bạn
thực sự khởi động ứng dụng của bạn là tốt hơn.
Kiến trúc JVM cho chúng ta làm điều này thuận tiện bằng cách làm việc với
việc thực hiện trình nạp lớp (classloader). Khi sử dụng các dấu móc của trình nạp
lớp, bạn có thể ngăn chặn quá trình nạp các lớp vào JVM và chuyển đổi các biểu
diễn lớp trước khi chúng thực sự được nạp. Để minh họa cách làm việc này, đầu
tiên tôi sẽ giải thích việc chặn nạp lớp trực tiếp, sau đó chỉ ra cách Javassist cung
cấp một phím tắt thuận tiện để bạn có thể sử dụng trong các ứng dụng của bạn.
Theo cách này, tôi sẽ sử dụng các đoạn mã từ các bài viết trước trong loạt bài này.
Đừng bỏ lỡ phần còn lại của loạt bài này
Phần 1, "Các lớp Java và nạp lớp" (04.2003)
Phần 2, "Giới thiệu sự phản chiếu" (06.2003)
Phần 3, "Ứng dụng sự phản chiếu" (07.2003)
Phần 4, "Chuyển đổi lớp bằng Javassist" (09.2003)


Phần 6, "Các thay đổi hướng-khía cạnh với Javassist" (03.2004)
Phần 7, "Kỹ thuật bytecode với BCEL" (04.2004)
Phần 8, "Thay thế sự phản chiếu bằng việc tạo mã" (06.2004)
Vùng nạp
Bình thường, bạn chạy một ứng dụng Java bằng cách xác định lớp chính như là
một tham số cho JVM. Điều này làm việc tốt với các hoạt động tiêu chuẩn, nhưng
không cung cấp cách nối bất kỳ đúng lúc vào quá trình nạp lớp có ích cho hầu hết
các ứng dụng. Như tôi đã thảo luận trong Phần 1 "Các lớp và việc nạp lớp," nhiều
lớp được nạp ngay trước khi lớp chính của bạn bắt đầu thực hiện. Việc ngăn chặn
nạp các lớp này đòi hỏi một mức gián tiếp trong việc thực hiện chương trình.
May mắn thay, rất dễ dàng để sao chép công việc JVM đã thực hiện trong khi chạy
lớp chính của ứng dụng của bạn. Tất cả những thứ mà bạn cần làm là sử dụng sự
phản chiếu (như đã trình bày trong Phần 2) để trước tiên tìm phương thức tĩnh
main() trong lớp cụ thể, sau đó gọi nó bằng các đối số dòng lệnh mong muốn. Liệt
kê 1 đưa ra mã ví dụ để thực hiện điều này (tôi đã để ngoài các phương thức nhập
khẩu và các lỗi ngoại lệ để giữ cho đoạn mã này ngắn gọn):

Liệt kê 1. Trình chạy (runner) ứng dụng Java

public class Run
{
public static void main(String[] args) {
if (args.length >= 1) {
try {

// load the target class to be run
Class clas = Run.class.getClassLoader().
loadClass(args[0]);

// invoke "main" method of target class

Class[] ptypes =
new Class[] { args.getClass() };
Method main =
clas.getDeclaredMethod("main", ptypes);
String[] pargs = new String[args.length-1];
System.arraycopy(args, 1, pargs, 0, pargs.length);
main.invoke(null, new Object[] { pargs });

} catch
}

} else {
System.out.println
("Usage: Run main-class args ");
}
}
}

Để chạy ứng dụng Java của bạn khi sử dụng lớp này, bạn chỉ cần đặt tên nó làm
đích cho lệnh java, tiếp sau nó là lớp chính cho ứng dụng của bạn và bất kỳ đối số
nào mà bạn muốn chuyển tới ứng dụng của bạn. Nói cách khác, nếu lệnh mà bạn
sử dụng để khởi chạy ứng dụng Java của bạn thường là:
java test.Test arg1 arg2 arg3


Thì thay vào đó bạn khởi chạy nó khi sử dụng lớp Run bằng lệnh:
java Run test.Test arg1 arg2 arg3


Chặn nạp lớp

Thật đúng với riêng nó, lớp Run nhỏ bé từ Liệt kê 1 rất không thực sự có ích. Để
hoàn thành mục tiêu của tôi về việc chặn quá trình nạp lớp chúng ta cần phải tiến
một bước xa hơn, bằng cách định nghĩa và sử dụng trình nạp lớp riêng của mình
cho các lớp ứng dụng.
Hỏi chuyên gia: Dennis Sosnoski về các vấn đề JVM và bytecode
Đối với các ý kiến hay các câu hỏi về tài liệu được trình bày trong loạt bài này,
cũng như bất cứ điều gì khác có liên quan đến Java bytecode, định dạng lớp nhị
phân Java hoặc các vấn đề JVM chung, hãy truy cập vào diễn đàn thảo luận JVM
và Bytecode, do Dennis Sosnoski kiểm soát.
Như chúng ta đã thảo luận trong Phần 1, các trình nạp lớp sử dụng một hệ thống
phân cấp có cấu trúc cây. Mỗi trình nạp lớp (trừ trình nạp lớp gốc được JVM sử
dụng cho các lớp Java cốt lõi) có trình nạp lớp cha mẹ. Các trình nạp lớp có nhiệm
vụ xác nhận lại trình nạp lớp cha mẹ của chúng trước khi nạp một lớp cho riêng
mình, để ngăn ngừa các xung đột có thể nảy sinh khi cùng một lớp được nạp bởi
nhiều hơn một trình nạp lớp trong một hệ thống phân cấp. Quá trình này xác nhận
lại với trình nạp lớp cha mẹ đầu tiên được gọi là delegation (ủy quyền) các trình
nạp lớp ủy quyền trách nhiệm để nạp một lớp cho trình nạp lớp gần với trình nạp
lớp gốc nhất có quyền truy cập vào thông tin lớp đó.
Khi chương trình Run từ Liệt kê 1 bắt đầu thực hiện, nó đã được trình nạp lớp Hệ
thống (System) mặc định cho JVM (JVM loại bỏ đường dẫn lớp-classpath mà bạn
xác định) nạp. Để tuân theo nguyên tắc ủy quyền nạp lớp này, chúng ta cần phải
tạo cho trình nạp lớp của mình một sự thay thế thật sự cho trình nạp lớp System,
khi sử dụng tất cả các thông tin đường dẫn lớp tương tự và ủy thác cho các trình
nạp lớp cha mẹ giống nhau. May mắn thay, lớp java.net.URLClassLoader được
các JVM hiện hành sử dụng để triển khai thực hiện trình nạp lớp System cung cấp
một cách dễ dàng để lấy ra thông tin đường dẫn lớp, khi sử dụng phương thức
getURLs() Để viết các trình nạp lớp của chúng ta, chúng ta có thể chỉ phân lớp
java.net.URLClassLoadervà khởi tạo lớp cơ sở để sử dụng cùng một đường dẫn
lớp và trình nạp lớp cha mẹ như là trình nạp lớp System để nạp lớp chính. Liệt kê
2 cho thấy việc thực hiện thực sự của cách tiếp cận này:


Liệt kê 2. Một trình nạp lớp dài dòng

public class VerboseLoader extends URLClassLoader
{
protected VerboseLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}

public Class loadClass(String name)
throws ClassNotFoundException {
System.out.println("loadClass: " + name);
return super.loadClass(name);
}

protected Class findClass(String name)
throws ClassNotFoundException {
Class clas = super.findClass(name);
System.out.println("findclass: loaded " + name +
" from this loader");
return clas;
}

public static void main(String[] args) {
if (args.length >= 1) {
try {

// get paths to be used for loading
ClassLoader base =
ClassLoader.getSystemClassLoader();

URL[] urls;
if (base instanceof URLClassLoader) {
urls = ((URLClassLoader)base).getURLs();
} else {
urls = new URL[]
{ new File(".").toURI().toURL() };
}

// list the paths actually being used
System.out.println("Loading from paths:");
for (int i = 0; i < urls.length; i++) {
System.out.println(" " + urls[i]);
}

// load target class using custom class loader
VerboseLoader loader =
new VerboseLoader(urls, base.getParent());
Class clas = loader.loadClass(args[0]);

// invoke "main" method of target class
Class[] ptypes =
new Class[] { args.getClass() };
Method main =
clas.getDeclaredMethod("main", ptypes);
String[] pargs = new String[args.length-1];
System.arraycopy(args, 1, pargs, 0, pargs.length);
Thread.currentThread().
setContextClassLoader(loader);
main.invoke(null, new Object[] { pargs });


} catch
}

} else {
System.out.println
("Usage: VerboseLoader main-class args ");
}
}
}

Chúng ta đã phân lớp java.net.URLClassLoader bằng lớp riêng VerboseLoader
của chúng ta để liệt kê ra tất cả các lớp đang được nạp, ghi nhận những lớp nào đã
được nạp bởi cá thể trình nạp này (chứ không phải là một trình nạp lớp cha mẹ ủy
quyền). Ở đây một lần nữa tôi đã bỏ qua các phương thức nhập khẩu và các lỗi
ngoại lệ để giữ cho đoạn mã ngắn gọn.
Hai phương thức đầu tiên trong lớp VerboseLoader, loadClass() và findClass() là
quan trọng hơn các phương thức của trình nạp lớp tiêu chuẩn. Phương thức
loadClass() được gọi cho mỗi lớp được yêu cầu từ trình nạp lớp. Trong trường hợp
này, chúng ta dùng nó chỉ để in một thông báo ra bàn điều khiển và sau đó gọi
phiên bản lớp cơ sở để xử lý thực sự. Phương thức lớp cơ sở triển khai thực hiện
hành vi ủy quyền của trình nạp lớp tiêu chuẩn, đầu tiên kiểm tra xem trình nạp lớp
cha mẹ có thể nạp lớp cần thiết không và chỉ cố gắng nạp lớp trực tiếp bằng cách
sử dụng phương thức findClass() có bảo vệ nếu trình nạp lớp cha mẹ bị hỏng. Đối
với việc thực hiện VerboseLoader của findClass(), trước tiên chúng ta gọi việc
thực hiện lớp cơ sở quan trọng hơn, sau đó in ra một thông báo nếu cuộc gọi thành
công (trả về mà không đưa ra một lỗi ngoại lệ).
Phương thức main() của VerboseLoader hoặc nhận được danh sách các địa chỉ
URL của đường dẫn lớp từ trình nạp được sử dụng cho lớp đang có hoặc, nếu
được sử dụng với một trình nạp không có một cá thể URLClassLoader, thì chỉ cần
sử dụng thư mục hiện tại làm lối vào đường dẫn lớp duy nhất. Dù bằng cách nào đi

nữa, nó sẽ liệt kê ra các đường dẫn đang được sử dụng trên thực tế, sau đó tạo một
cá thể của lớp VerboseLoader và sử dụng nó để nạp lớp đích có tên trên dòng lệnh.
Phần còn lại của logic này, để tìm và gọi phương thức main() của lớp đích, giống
như mã Run của Liệt kê 1.
Liệt kê 3 cho thấy một ví dụ về dòng lệnh VerboseLoader và kết quả được sử dụng
để gọi các ứng dụng Run từ Liệt kê 1:

Liệt kê 3. Ví dụ kết quả từ chương trình của Liệt kê 2

[dennis]$ java VerboseLoader Run
Loading from paths:
file:/home/dennis/writing/articles/devworks/dynamic/code5/
loadClass: Run
loadClass: java.lang.Object
findclass: loaded Run from this loader
loadClass: java.lang.Throwable
loadClass: java.lang.reflect.InvocationTargetException
loadClass: java.lang.IllegalAccessException
loadClass: java.lang.IllegalArgumentException
loadClass: java.lang.NoSuchMethodException
loadClass: java.lang.ClassNotFoundException
loadClass: java.lang.NoClassDefFoundError
loadClass: java.lang.Class
loadClass: java.lang.String
loadClass: java.lang.System
loadClass: java.io.PrintStream
Usage: Run main-class args

Trong trường hợp này, lớp duy nhất được VerboseLoader nạp trực tiếp là lớp Run.
Tất cả các lớp khác được lớp Run sử dụng là các lớp Java lõi, các lớp lõi này được

nạp bằng sự ủy quyền thông qua trình nạp lớp cha mẹ. Hầu hết nếu không phải
tất cả các lớp Java lõi trên thực tế được nạp trong quá trình tự khởi động của
ứng dụng VerboseLoader vì vậy trình nạp lớp cha mẹ sẽ chỉ trả về một tham chiếu
đến cá thể java.lang.Class được tạo ra trước đó.
Javassist chặn
VerboseClassloader từ Liệt kê 2 cho thấy những điều căn bản về việc chặn nạp
lớp. Để thay đổi các lớp khi chúng đang được nạp, chúng ta có thể lấy thêm việc
này, thêm mã vào phương thức findClass() để truy cập tệp lớp nhị phân như một
tài nguyên và sau đó làm việc với các dữ liệu nhị phân. Trên thực tế Javassist bao
gồm mã để thực hiện trực tiếp kiểu chặn này, vì vậy hơn là tiếp tục ví dụ này,
chúng ta sẽ xem cách sử dụng việc thực hiện Javassist để thay thế.
Việc chặn nạp lớp với Javassist xây dựng trên cùng lớp javassist.ClassPool mà
chúng ta đã làm trong Phần 4. Trong bài viết này, chúng ta đã yêu cầu một lớp
theo tên trực tiếp từ ClassPool, tìm lại việc biểu diễnJavassist của lớp đó dưới
dạng một cá thể javassist.CtClass. Mặc dù, đây không phải là cách duy nhất để sử
dụng một ClassPool Javassist cũng cung cấp một trình nạp lớp có sử dụng
ClassPool như là nguồn dữ liệu lớp của nó, dưới dạng lớp javassist.Loader.
Để cho phép bạn làm việc với các lớp khi chúng đang được nạp ClassPool sử dụng
một mẫu Trình quan sát (Observer). Bạn có thể chuyển một cá thể của giao diện
trình quan sát mong muốn, javassist.Translator, tới hàm tạo ClassPool. Mỗi khi
một lớp mới được yêu cầu từ ClassPool nó gọi phương thức onWrite() của Trình
quan sát để có thể thay đổi biểu diễn lớp trước khi nó được ClassPool phân phát.
Lớp javassist.Loader này có phương thức run() thuận tiện để nạp một lớp đích và
gọi phương thức main() của lớp đó với một mảng các đối số được cung cấp (như
trong mã của Liệt kê 1). Liệt kê 4 chứng tỏ việc sử dụng các lớp Javassist và
phương thức này để nạp và chạy một lớp ứng dụng đích. Việc thực hiện trình quan
sát javassist.Translator đơn giản trong trường hợp này chỉ in ra một thông báo về
lớp đang được yêu cầu.

Liệt kê 4. Trình chạy ứng dụng Javassist


public class JavassistRun
{
public static void main(String[] args) {
if (args.length >= 1) {
try {

// set up class loader with translator
Translator xlat = new VerboseTranslator();
ClassPool pool = ClassPool.getDefault(xlat);
Loader loader = new Loader(pool);

// invoke "main" method of target class
String[] pargs = new String[args.length-1];
System.arraycopy(args, 1, pargs, 0, pargs.length);
loader.run(args[0], pargs);

} catch
}

} else {
System.out.println
("Usage: JavassistRun main-class args ");
}
}

public static class VerboseTranslator implements Translator
{
public void start(ClassPool pool) {}


public void onWrite(ClassPool pool, String cname) {
System.out.println("onWrite called for " + cname);
}
}
}

Dưới đây là một ví dụ về dòng lệnh JavassistRun và kết quả, khi sử dụng nó để gọi
ứng dụng Run từ Liệt kê 1:
[dennis]$java -cp .:javassist.jar JavassistRun Run
onWrite called for Run
Usage: Run main-class args



Tính thời gian chạy
Sự thay đổi tính thời gian của phương thức mà chúng ta đã xét trong Phần 4 có thể
là một công cụ hữu ích để cô lập các vấn đề hiệu năng, nhưng nó thực sự cần một
giao diện linh hoạt hơn. Trong bài viết đó, chúng ta đã chuyển đổi lớp và tên
phương thức như là các tham số dòng lệnh tới chương trình của tôi, chương trình
này đã nạp tệp lớp nhị phân, đã thêm vào mã tính thời gian, sau đó đã viết lại lớp
đó sau. Đối với bài này, chúng ta sẽ chuyển đổi mã để sử dụng một cách tiếp cận
thay đổi thời gian nạp và để hỗ trợ phối hợp mẫu cho việc xác định các lớp và các
phương thức có tính thời gian.
Việc thay đổi mã để xử lý các thay đổi như các lớp được nạp rất dễ dàng. Khi tách
javassist.Translator khỏi Liệt kê 4, chúng ta chỉ có thể gọi phương thức bổ sung
thêm các thông tin tính thời gian từ onWrite() khi tên lớp đang được viết khớp với
tên lớp đích. Liệt kê 5 hiển thị điều này (không có tất cả các chi tiết về
addTiming() xem Phần 4 của loạt bài này).

Liệt kê 5. Thêm mã tính thời gian vào thời gian-nạp


public class TranslateTiming
{
private static void addTiming(CtClass clas, String mname)
throws NotFoundException, CannotCompileException {

}

public static void main(String[] args) {
if (args.length >= 3) {
try {

// set up class loader with translator
Translator xlat =
new SimpleTranslator(args[0], args[1]);
ClassPool pool = ClassPool.getDefault(xlat);
Loader loader = new Loader(pool);

// invoke "main" method of target class
String[] pargs = new String[args.length-3];
System.arraycopy(args, 3, pargs, 0, pargs.length);
loader.run(args[2], pargs);

} catch (Throwable ex) {
ex.printStackTrace();
}

} else {
System.out.println("Usage: TranslateTiming" +
" class-name method-mname main-class args ");

}
}

public static class SimpleTranslator implements Translator
{
private String m_className;
private String m_methodName;

public SimpleTranslator(String cname, String mname) {
m_className = cname;
m_methodName = mname;
}

public void start(ClassPool pool) {}

public void onWrite(ClassPool pool, String cname)
throws NotFoundException, CannotCompileException {
if (cname.equals(m_className)) {
CtClass clas = pool.get(cname);
addTiming(clas, m_methodName);
}
}
}
}

Các phương thức mẫu
Ngoài việc để cho mã tính thời gian của phương thức này làm việc trong thời gian-
nạp, như thể hiện trong Liệt kê 5, thật tốt để thêm tính linh hoạt trong việc xác
định (các) phương thức được tính thời gian. Tôi bắt đầu triển khai thực hiện việc
này bằng cách sử dụng biểu thức chính quy khớp với sự hỗ trợ trong gói

java.util.regex của Java 1.4, sau đó nhận thấy nó đã không thực sự đem lại cho tôi
phần nào sự linh hoạt mà tôi muốn. Vấn đề là một phần các mẫu có ý nghĩa với tôi
để chọn các lớp và các phương thức đã thay đổi không hoàn toàn giống với mô
hình biểu thức chính quy.
Vì vậy một phần các mẫu có ý nghĩa cho việc lựa chọn các lớp và các phương thức
là gì? Những gì mà tôi muốn là khả năng sử dụng bất kỳ trong số một vài đặc điểm
của lớp và phương thức trong các mẫu này, bao gồm lớp thực tế và tên phương
thức, kiểu trả về và (các) kiểu tham số gọi. Mặt khác, tôi đã không thực sự cần các
phép so sánh linh hoạt trên các tên và các kiểu một phép so sánh bằng đơn giản
đã giải quyết hầu hết các trường hợp tôi quan tâm đến và thêm các ký tự đại diện
cơ bản cho các phép so sánh đã quan tâm còn lại. Cách tiếp cận dễ nhất để xử lý
điều này là làm cho các mẫu trông giống như các khai báo phương thức Java
chuẩn, với một vài phần mở rộng.
Đối với một số ví dụ về cách tiếp cận này, đây là một vài ví dụ sẽ phù hợp với
phương thức String buildString(int) của lớp test.StringBuilder:
java.lang.String test.StringBuilder.buildString(int)

test.StringBuilder.buildString(int)
*buildString(int)
*buildString

Mẫu chung của các mẫu này trước tiên là một kiểu trả về tùy chọn (với văn bản
chính xác), sau đó mẫu lớp và tên phương thức được kết hợp (bằng các ký tự "*" )
và cuối cùng là danh sách (các) kiểu tham số (với văn bản chính xác). Nếu kiểu trả
về xuất hiện, nó phải được tách ra khỏi tên phương thức thích hợp bằng một
khoảng trống, trong khi danh sách các tham số đi theo tên phương thức thích hợp.
Để làm cho tham số phối hợp linh hoạt, tôi thiết lập nó để làm việc theo hai cách.
Nếu các tham số đã cho là một danh sách có các dấu ngoặc đơn bao quanh, chúng
phải giống hệt với các tham số phương thức. Thay vào đó nếu chúng được các dấu
ngoặc vuông ("[]"), bao quanh, tất cả các kiểu đã liệt kê này phải xuất hiện như là

các tham số của một phương thức phù hợp, nhưng phương thức này có thể sử dụng
chúng theo thứ tự bất kỳ và cũng có thể sử dụng các tham số bổ sung thêm. Vì vậy
*buildString(int, java.lang.String) giống với bất kỳ phương thức nào có một tên
kết thúc bằng "buildString" và lấy chính xác hai tham số, một int và một String,
theo đúng thứ tự. *buildString[int,java.lang.String] giống các phương thức có
cùng các tên, nhưng dùng hai hoặc nhiều tham số hơn, một trong các tham số đó là
int và tham số khác là java.lang.String.
Liệt kê 6 đưa ra một phiên bản rút gọn của lớp con javassist.Translator mà tôi đã
viết để xử lý các ví dụ này. Mã khớp thực sự thực ra không liên quan đến bài viết
này, nhưng nó có trong tệp tải về (xem Tài nguyên) nếu bạn muốn xem nó kỹ hơn
hoặc sử dụng nó cho mình. Lớp chương trình chính sử dụng TimingTranslator này
là BatchTiming, cũng có trong tệp tải về này.

Liệt kê 6. Trình dịch giống-mẫu

public class TimingTranslator implements Translator
{
public TimingTranslator(String pattern) {
// build matching structures for supplied pattern

}

private boolean matchType(CtMethod meth) {

}

private boolean matchParameters(CtMethod meth) {

}


private boolean matchName(CtMethod meth) {

}

private void addTiming(CtMethod meth) {

}

public void start(ClassPool pool) {}

public void onWrite(ClassPool pool, String cname)
throws NotFoundException, CannotCompileException {

// loop through all methods declared in class
CtClass clas = pool.get(cname);
CtMethod[] meths = clas.getDeclaredMethods();
for (int i = 0; i < meths.length; i++) {

// check if method matches full pattern
CtMethod meth = meths[i];
if (matchType(meth) &&
matchParameters(meth) && matchName(meth)) {

// handle the actual timing modification
addTiming(meth);
}
}
}
}




Tiếp theo
Trong hai bài viết mới đây, bây giờ bạn đã thấy cách sử dụng Javassist để xử lý
các phép chuyển đổi cơ bản. Với bài viết tiếp theo, chúng ta sẽ xem xét các tính
năng nâng cao của khung công tác này để cung cấp các kỹ thuật tìm kiếm-và-thay
thế cho việc chỉnh sửa bytecode. Các tính năng này tạo ra các thay đổi có hệ thống
để lập trình hành vi dễ dàng, bao gồm cả những thay đổi như là chặn tất cả các
cuộc gọi đến một phương thức hay tất cả các truy cập của một trường. Chúng là
chìa khóa để hiểu tại sao Javassist là một khung công tác quan trọng cho việc hỗ
trợ hướng-khía cạnh trong các chương trình Java. Hãy quay lại vào tháng tới để
xem cách bạn có thể sử dụng Javassist để mở khóa các khía cạnh trong các ứng
dụng của bạn.


Mục lục

 Vùng nạp
 Tính thời gian chạy
 Tiếp theo

×