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

Động lực học lập trình Java, Phần 4: Chuyển đổi lớp bằng Javassist pps

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 (203.28 KB, 17 trang )

Động lực học lập trình Java, Phần 4: Chuyển đổi lớp bằng Javassist
Sử dụng Javassist để chuyển đổi các phương thức theo bytecode
Dennis Sosnoski, Nhà tư vấn, Sosnoski Software Solutions, Inc.
Tóm tắt: Thật buồn tẻ với các lớp Java chỉ thực hiện theo cách mã nguồn đã được
viết phải không? Sau đó, hãy vui vẻ lên, bởi vì bạn sắp sửa thấy việc kết hợp các
lớp theo các hình dạng chưa bao giờ được trình biên dịch dự kiến! Trong bài viết
này, nhà tư vấn Java Dennis Sosnoski đóng góp loại bài động lực học lập trình
Java của mình vào việc tăng nhanh tốc độ xem xét Javassist, thư viện thao tác mã
byte (bytecode), đây là cơ sở cho các tính năng lập trình hướng-khía cạnh được bổ
sung cho máy chủ ứng dụng JBoss được sử dụng rộng rãi. Bạn sẽ tìm ra những
điều cơ bản về việc chuyển đổi các lớp hiện có với Javassist và nhận thấy cả sức
mạnh lẫn hạn chế của cách tiếp cận mã nguồn mở của khung công tác này với hoạt
động lớp (classworking).
Sau khi trình bày những điều căn bản của việc định dạng lớp Java và truy cập
trong lúc chạy qua phản chiếu, đây là lúc để di chuyển loạt bài này theo hướng tới
nhiều chủ đề cao cấp hơn. Trong số tháng này tôi sẽ bắt đầu vào phần thứ hai của
loạt bài, ở đây các thông tin về lớp Java chỉ trở thành một dạng cấu trúc dữ liệu
khác được các ứng dụng xử lí. Tôi sẽ gọi toàn bộ lĩnh vực của chủ đề này là hoạt
động lớp (classworking).
Tôi sẽ bắt đầu trình bày hoạt động lớp với thư viện thao tác bytecode Javassist.
Javassist không phải là thư viện duy nhất để làm việc với bytecode, mà nó còn có
một tính năng cụ thể làm cho nó trở thành một điểm khởi đầu quan trọng cho các
thí nghiệm hoạt động lớp: bạn có thể sử dụng Javassist để làm thay đổi bytecode
của một lớp Java mà trên thực tế không cần tìm hiểu bất cứ điều gì về kiến trúc
bytecode hoặc kiến trúc máy ảo Java (JVM). Đây là một điều may mắn lẫn trong
một số chi tiết cụ thể nói chung tôi không tán thành can thiệp vào công nghệ mà
bạn không hiểu nhưng chắc chắn nó làm cho việc thao tác bytecode có khả năng
truy cập nhiều hơn so với các khung công tác mà ở đó bạn làm việc ở mức các
hướng dẫn riêng.
Những điều cơ bản về Javassist
Javassist cho phép bạn kiểm tra, chỉnh sửa và tạo các lớp Java nhị phân. Khía cạnh


kiểm tra chủ yếu lặp lại chính xác những gì có sẵn trực tiếp trong Java thông qua
Reflection API, nhưng việc có cách khác để truy cập thông tin này là rất có ích khi
trên thực tế bạn đang sửa đổi các lớp thay vì chỉ cần thực hiện chúng. Điều này là
do thiết kế JVM không cung cấp cho bạn bất kỳ quyền truy cập nào vào dữ liệu
lớp thô sau khi nó được nạp vào JVM. Nếu bạn sắp làm việc với các lớp như là dữ
liệu, bạn cần phải làm như thế bên ngoài JVM.
Đừ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 5, "Việc chuyển các lớp đang hoạt động" (02.2004)
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)
Javassist sử dụng lớp javassist.ClassPool để theo dõi và kiểm soát các lớp bạn
đang thao tác. Lớp này làm việc rất giống như một trình nạp lớp (classloader) của
JVM, nhưng có sự khác biệt quan trọng khác hơn việc kết nối các lớp đã nạp để
thực hiện như một phần của ứng dụng của bạn, nhóm lớp giúp cho các lớp đã nạp
có thể sử dụng như là dữ liệu thông qua Javassist API. Bạn có thể sử dụng một
nhóm lớp mặc định để nạp từ đường dẫn tìm kiếm JVM hoặc xác định một đường
dẫn để tìm kiếm danh sách các đường dẫn riêng của bạn. Bạn thậm chí có thể tải
trực tiếp các lớp nhị phân từ các mảng hoặc luồng byte và tạo các lớp mới từ đầu.
Các lớp được nạp trong một nhóm lớp được các cá thể javassist.CtClass đại diện.
Như với lớp Java tiêu chuẩn java.lang.Class, CtClass cung cấp các phương thức để
kiểm tra dữ liệu lớp như các trường và các phương thức. Đó chỉ là sự khởi đầu cho
CtClass, tuy nhiên, cũng định nghĩa các phương thức để thêm vào các trường, các
phương thức và các hàm tạo mới cho lớp đó và để thay đổi tên lớp, siêu lớp và các
giao diện. Thật kỳ quặc, Javassist không cung cấp bất kỳ cách nào để xóa các
trường, các phương thức hoặc các hàm tạo từ một lớp.
Các trường, các phương thức và các hàm tạo được các cá thể tương ứng

javassist.CtField, javassist.CtMethod và javassist.CtConstructor biểu diễn. Các lớp
này xác định các phương thức để sửa đổi tất cả các khía cạnh của mục được lớp đó
đại diện, bao gồm cả phần bytecode thực sự của một phương thức hoặc hàm tạo.
Mã nguồn của tất cả bytecode
Javassist cho phép bạn thay thế hoàn toàn phần thân bytecode của một phương
thức hoặc hàm tạo hoặc thêm vào khả năng chọn lọc bytecode ở đầu hoặc cuối của
phần thân hiện có (cùng với một cặp các biến khác cho các hàm tạo). Dù bằng
cách nào, các bytecode mới được chuyển qua như là một câu lệnh hay khối mã
nguồn giống như Java trong một String. Các phương thức Javassist biên dịch có
hiệu quả mã nguồn mà bạn cung cấp trong bytecode của Java, sau đó chúng chèn
bytecode này vào trong phần thân của phương thức hoặc hàm tạo đích.
Mã nguồn được Javassist chấp nhận không khớp chính xác với ngôn ngữ Java,
nhưng sự khác biệt chính là việc bổ sung một số trình nhận dạng đặc biệt dùng để
mô tả các tham số của phương thức hoặc hàm tạo, giá trị trả về phương thức và
các mục khác mà bạn có thể muốn sử dụng trong mã chèn vào của bạn. Tất cả các
trình nhận dạng đặc biệt này bắt đầu bằng biểu tượng $, vì vậy chúng sẽ không can
thiệp tới bất cứ điều gì mà bạn đã làm khác đi trong mã của bạn.
Cũng có một số hạn chế về những gì bạn có thể làm trong mã nguồn mà bạn
chuyển tới Javassist. Hạn chế đầu tiên là định dạng thực tế, nó phải là một câu
lệnh hay một khối. Đây là một hạn chế không tốt đối với hầu hết các mục đích, vì
bạn có thể đặt bất cứ chuỗi các câu lệnh nào mà bạn muốn trong một khối. Dưới
đây là một ví dụ khi sử dụng trình nhận dạng Javassist đặc biệt cho hai giá trị tham
số phương thức đầu tiên để hiển thị cách điều này hoạt động:
{
System.out.println("Argument 1: " + $1);
System.out.println("Argument 2: " + $2);
}

Một hạn chế đáng kể hơn về mã nguồn là không có cách nào tham chiếu đến các
biến cục bộ đã khai báo ngoài câu lệnh hoặc khối được thêm vào. Điều này có

nghĩa rằng nếu bạn đang thêm mã ở cả hai phần bắt đầu và kết thúc của một
phương thức, ví dụ, nói chung bạn sẽ không có khả năng chuyển các thông tin từ
mã được thêm vào ở lúc bắt đầu đến mã được thêm vào ở lúc kết thúc. Có nhiều
cách giải quyết xung quanh hạn chế này, nhưng cách giải quyết rất lộn xộn nói
chung bạn cần phải tìm một cách để kết hợp mã riêng chèn vào trong một khối.


Hoạt động lớp với Javassist
Với một ví dụ về việc áp dụng Javassist, tôi sẽ sử dụng một nhiệm vụ mà tôi đã
thường xuyên xử lý trực tiếp trong mã nguồn: đo thời gian đã mất để thực hiện
một phương thức. Phép đo này là đủ dễ dàng để thực hiện trong mã nguồn; bạn chỉ
cần ghi lại thời gian hiện tại ở đầu phương thức, sau đó kiểm tra lại thời gian hiện
tại ở cuối phương thức và tìm thấy sự khác biệt giữa hai giá trị đó. Nếu bạn không
có mã nguồn, sẽ khó khăn hơn nhiều để có được kiểu thông tin tính thời gian này.
Đó là nơi mà hoạt động lớp có ích nó cho phép bạn thực hiện các thay đổi giống
như điều này với phương thức bất kỳ, mà không cần mã nguồn.
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.
Liệt kê 1 hiển thị một phương thức ví dụ (xấu) mà tôi sẽ sử dụng như là một thí
nghiệm tính toán thời gian của tôi: phương thức buildString của lớp StringBuilder.
Phương thức này xây dựng một String với độ dài bất kì bằng cách thực hiện chính
xác những gì mà bất kỳ chuyên gia hiệu năng Java nào sẽ nói bạn không phải làm -
- nó nhiều lần gắn thêm chỉ một ký tự vào cuối chuỗi để tạo một chuỗi dài hơn. Do
các chuỗi không thể thay đổi được, cách tiếp cận này có nghĩa là một chuỗi mới sẽ
được xây dựng mỗi lần qua vòng lặp, với các dữ liệu được sao chép từ chuỗi cũ và
chỉ một ký tự được thêm vào cuối. Tác động cuối cùng là phương thức này gặp
phải chi phí hoạt động càng ngày càng nhiều khi nó được sử dụng để tạo các chuỗi

dài hơn.

Liệt kê 1. Phương thức có tính giờ
public class StringBuilder
{
private String buildString(int length) {
String result = "";
for (int i = 0; i < length; i++) {
result += (char)(i%26 + 'a');
}
return result;
}

public static void main(String[] argv) {
StringBuilder inst = new StringBuilder();
for (int i = 0; i < argv.length; i++) {
String result = inst.buildString(Integer.parseInt(argv[i]));
System.out.println("Constructed string of length " +
result.length());
}
}
}

Thêm tính thời gian phương thức
Vì tôi có sẵn mã nguồn cho phương thức này, tôi sẽ cho bạn thấy cách tôi sẽ trực
tiếp thêm vào thông tin tính thời gian. Điều này cũng sẽ dùng làm mô hình cho
những gì mà tôi muốn làm khi sử dụng Javassist. Liệt kê 2 cho thấy phương thức
buildString() có bổ sung thêm tính thời gian. Điều này chẳng có giá trị thay đổi
nào. Mã được thêm vào này lưu trữ thời gian bắt đầu cho một biến cục bộ, sau đó
tính thời gian trôi qua ở cuối của phương thức và in nó ra bàn điều khiển.


Liệt kê 2. Phương thức có tính thời gian
private String buildString(int length) {
long start = System.currentTimeMillis();
String result = "";
for (int i = 0; i < length; i++) {
result += (char)(i%26 + 'a');
}
System.out.println("Call to buildString took " +
(System.currentTimeMillis()-start) + " ms.");
return result;
}

Thực hiện nó với Javassist
Khai thác cùng tác dụng như khi sử dụng Javassist để xử lí bytecode lớp có vẻ như
nó sẽ dễ dàng. Javassist cung cấp nhiều cách để thêm mã vào ở đầu và cuối của
các phương thức, sau tất cả, mã này phải chính xác với những gì mà tôi đã thực
hiện trong mã nguồn để thêm thông tin tính thời gian cho phương thức này.
Tuy nhiên, cũng có một khó khăn. Khi tôi đã mô tả cách Javassist để cho phép bạn
thêm mã, tôi đã nói rằng mã thêm vào không thể tham chiếu đến các biến cục bộ
đã xác định ở những nơi khác trong phương thức. Sự hạn chế này ngăn cản tôi
triển khai thực hiện mã tính thời gian trong Javassist theo cùng cách mà tôi đã thực
hiện trong mã nguồn; trong trường hợp đó, tôi đã xác định một biến cục bộ mới
trong mã được thêm vào ở lúc bắt đầu và đã tham chiếu biến đó trong mã được
thêm vào ở cuối.
Vậy tôi có thể sử dụng cách tiếp cận khác nào để nhận được tác dụng tương tự?
Đúng, tôi có thể thêm một trường thành viên mới vào lớp đó và sử dụng nó thay
cho biến cục bộ. Tuy nhiên đó là hơi hướng của giải pháp và phạm phải một số
hạn chế về sử dụng chung. Ví dụ, hãy xem điều gì sẽ xảy ra với một phương thức
đệ quy. Mỗi lần phương thức tự gọi chính nó, giá trị thời gian bắt đầu được lưu trữ

từ lần gọi cuối cùng sẽ bị ghi đè và bị mất.
May mắn thay có một giải pháp tốt hơn. Tôi có thể giữ cho mã phương thức ban
đầu không thay đổi và chỉ cần thay đổi tên phương thức, sau đó thêm một phương
thức mới bằng cách sử dụng tên ban đầu. Phương thức interceptor (chặn) này có
thể sử dụng cùng chữ ký như phương thức ban đầu, kể cả trả về giá trị như nhau.
Liệt kê 3 cho thấy một phiên bản mã nguồn của phương thức này sẽ trông như sau:

Liệt kê 3. Thêm một phương thức chặn trong mã nguồn

private String buildString$impl(int length) {
String result = "";
for (int i = 0; i < length; i++) {
result += (char)(i%26 + 'a');
}
return result;
}
private String buildString(int length) {
long start = System.currentTimeMillis();
String result = buildString$impl(length);
System.out.println("Call to buildString took " +
(System.currentTimeMillis()-start) + " ms.");
return result;
}

Cách tiếp cận của việc sử dụng một phương thức chặn này hoạt động tốt với
Javassist. Vì toàn bộ phần thân của phương thức này là một khối, tôi có thể xác
định và sử dụng các biến cục bộ trong phần thân mà không có bất kỳ vấn đề nào.
Tạo mã nguồn cho phương thức chặn này cũng dễ dàng; chỉ cần một vài thay đổi
để làm việc với phương thức có thể bất kì.
Chạy phương thức chặn

Triển khai thực hiện các đoạn mã để thêm vào tính thời gian của phương thức sử
dụng một số các Javassist API đã mô tả trong những điều cơ bản về Javassist. Liệt
kê 4 cho thấy mã này, dưới dạng một ứng dụng lấy một cặp các đối số dòng lệnh
cho tên lớp và tên phương thức được tính thời gian. Phần thân phương thức main()
chỉ tìm thấy các thông tin lớp và sau đó chuyển nó tới phương thức addTiming()
để xử lý các sửa đổi thực sự. Phương thức addTiming() đầu tiên đặt lại tên cho
phương thức hiện tại bằng cách gắn thêm "$impl" vào cuối tên, sau đó tạo một bản
sao của phương thức khi sử dụng tên ban đầu. Sau đó nó thay thế thân phương
thức đã sao chép với mã có tính thời gian đang bao bọc một cuộc gọi đến phương
thức ban đầu đã đổi tên.

Liệt kê 4. Thêm phương thức chặn với Javassist
public class JassistTiming
{
public static void main(String[] argv) {
if (argv.length == 2) {
try {

// start by getting the class file and method
CtClass clas = ClassPool.getDefault().get(argv[0]);
if (clas == null) {
System.err.println("Class " + argv[0] + " not found");
} else {

// add timing interceptor to the class
addTiming(clas, argv[1]);
clas.writeFile();
System.out.println("Added timing to method " +
argv[0] + "." + argv[1]);


}

} catch (CannotCompileException ex) {
ex.printStackTrace();
} catch (NotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}

} else {
System.out.println("Usage: JassistTiming class method-name");
}
}

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

// get the method information (throws exception if method with
// given name is not declared directly by this class, returns
// arbitrary choice if more than one with the given name)
CtMethod mold = clas.getDeclaredMethod(mname);

// rename old method to synthetic name, then duplicate the
// method with original name for use as interceptor
String nname = mname+"$impl";
mold.setName(nname);
CtMethod mnew = CtNewMethod.copy(mold, mname, clas, null);

// start the body text generation by saving the start time

// to a local variable, then call the timed method; the
// actual code generated needs to depend on whether the
// timed method returns a value
String type = mold.getReturnType().getName();
StringBuffer body = new StringBuffer();
body.append("{\nlong start = System.currentTimeMillis();\n");
if (!"void".equals(type)) {
body.append(type + " result = ");
}
body.append(nname + "($$);\n");

// finish body text generation with call to print the timing
// information, and return saved value (if not void)
body.append("System.out.println(\"Call to method " + mname +
" took \" +\n (System.currentTimeMillis()-start) + " +
"\" ms.\");\n");
if (!"void".equals(type)) {
body.append("return result;\n");
}
body.append("}");

// replace the body of the interceptor method with generated
// code block and add it to class
mnew.setBody(body.toString());
clas.addMethod(mnew);

// print the generated code block just to show what was done
System.out.println("Interceptor method body:");
System.out.println(body.toString());
}

}

Xây dựng phần thân phương thức chặn sử dụng một java.lang.StringBuffer để tích
lũy phần văn bản thân (chỉ ra một cách thích hợp để xử lý xây dựng String , trái
với cách tiếp cận đã dùng trong StringBuilder). Phần thân văn bản thay đổi dựa
vào phương thức ban đầu có trả về một giá trị hay không. Nếu nó thực sự trả về
một giá trị, mã đã xây dựng lưu trữ giá trị đó trong một biến cục bộ sao cho nó có
thể được trả về ở cuối của phương thức chặn. Nếu phương thức ban đầu là kiểu
khoảng trống, thì chẳng có gì để lưu trữ cả và chẳng có gì để trả về từ phương thức
chặn.
Phần văn bản thân trên thực tế trông giống như mã Java tiêu chuẩn trừ các cuộc
gọi đến (được đổi tên) phương thức ban đầu. Đây là một dòng body.append(nname
+ "($$);\n"); trong đoạn mã đó, ở đây nname là tên đã sửa đổi cho phương thức
ban đầu. Trình nhận dạng $$ được sử dụng trong cuộc gọi là cách mà Javassist
trình bày danh sách các tham số cho phương thức trong xây dựng. Bằng cách sử
dụng trình nhận dạng này trong cuộc gọi đến phương thức ban đầu, tất cả các đối
số được cung cấp trong cuộc gọi này đến phương thức chặn được chuyển qua tới
phương thức ban đầu.
Liệt kê 5 cho thấy các kết quả của lần đầu tiên chạy chương trình StringBuilder ở
dạng chưa sửa đổi, sau đó chạy chương trình JassistTiming để thêm thông tin tính
thời gian và cuối cùng là chạy chương trình StringBuilder sau khi nó được sửa đổi.
Bạn có thể thấy cách StringBuilder chạy sau thay đổi về các thời gian thực hiện và
bao nhiêu lần tăng nhanh hơn so với chiều dài của chuỗi được xây dựng, do đoạn
mã xây dựng chuỗi không hiệu quả.

Liệt kê 5. Chạy các chương trình
[dennis]$ java StringBuilder 1000 2000 4000 8000 16000
Constructed string of length 1000
Constructed string of length 2000
Constructed string of length 4000

Constructed string of length 8000
Constructed string of length 16000

[dennis]$ java -cp javassist.jar:. JassistTiming StringBuilder buildString
Interceptor method body:
{
long start = System.currentTimeMillis();
java.lang.String result = buildString$impl($$);

System.out.println("Call to method buildString took " +
(System.currentTimeMillis()-start) + " ms.");
return result;
}
Added timing to method StringBuilder.buildString

[dennis]$ java StringBuilder 1000 2000 4000 8000 16000
Call to method buildString took 37 ms.
Constructed string of length 1000
Call to method buildString took 59 ms.
Constructed string of length 2000
Call to method buildString took 181 ms.
Constructed string of length 4000
Call to method buildString took 863 ms.
Constructed string of length 8000
Call to method buildString took 4154 ms.
Constructed string of length 16000



Tin tưởng vào mã nguồn, Luke?

Javassist thực hiện một công việc rất lớn làm cho hoạt động lớp dễ dàng bằng cách
cho phép bạn làm việc với mã nguồn chứ không phải là các danh sách hướng dẫn
bytecode thực sự. Nhưng điều dễ sử dụng này đi kèm với một số hạn chế. Như tôi
đã đề cập lại trong mã nguồn của tất cả các bytecode, mã nguồn được Javassist sử
dụng không chính xác là ngôn ngữ Java. Bên cạnh việc thừa nhận các trình nhận
dạng đặc biệt trong mã này, Javassist triển khai thực hiện kiểm tra thời gian biên
dịch lỏng lẻo trên mã hơn được đặc tả ngôn ngữ Java yêu cầu. Vì điều này, nó sẽ
tạo bytecode từ mã nguồn theo những cách có thể có các kết quả gây ngạc nhiên
nếu bạn không cẩn thận.
Ví dụ, Liệt kê 6 cho thấy những gì xảy ra khi tôi thay đổi kiểu của biến cục bộ
thường sử dụng cho thời gian bắt đầu phương thức trong mã chặn từ biến long
sang int. Javassist chấp nhận mã nguồn và biến đổi nó thành bytecode hợp lệ,
nhưng các thời gian kết quả là sai. Nếu bạn đã thử biên dịch trực tiếp nhiệm vụ
này vào trong một chương trình Java, bạn sẽ nhận được một lỗi biên dịch vì nó vi
phạm một trong các quy tắc của ngôn ngữ Java: một nhiệm vụ hẹp đòi hỏi một
khuôn mẫu.

Liệt kê 6. . Lưu trữ một biến long trong một biến int
[dennis]$ java -cp javassist.jar:. JassistTiming StringBuilder buildString
Interceptor method body:
{
int start = System.currentTimeMillis();
java.lang.Strin
g result = buildString$impl($$);
System.out.println("Call to method buildString took " +
(System.currentTimeMillis()-start) + " ms.");
return result;
}
Added timing to method StringBuilder.buildString
[dennis]$ java StringBuilder 1000 2000 4000 8000 16000

Call to method buildString took 1060856922184 ms.
Constructed string of length 1000
Call to method buildString took 1060856922172 ms.
Constructed string of length 2000
Call to method buildString took 1060856922382 ms.
Constructed string of length 4000
Call to method buildString took 1060856922809 ms.
Constructed string of length 8000
Call to method buildString took 1060856926253 ms.
Constructed string of length 16000

Tùy thuộc vào những gì bạn làm trong mã nguồn, thậm chí bạn có thể nhận được
Javassist để tạo bytecode không hợp lệ. Liệt kê 7 cho thấy một ví dụ về điều này, ở
đây tôi đã vá lỗi đoạn mã JassistTiming để luôn luôn xử lý phương thức có tính
thời gian như sự trả về một giá trị int. Javassist một lần nữa chấp nhận mã nguồn
mà không đưa ra lỗi, nhưng bytecode không thực hiện xác nhận tính hợp lệ khi tôi
cố gắng thực hiện nó.

Liệt kê 7. Lưu trữ một biến String vào một biến int
[dennis]$ java -cp javassist.jar:. JassistTiming StringBuilder buildString
Interceptor method body:
{
long start = System.currentTimeMillis();
int result = buildString$impl($$);
System.out.println("Call to method buildString took " +
(System.currentTimeMillis()-start) + " ms.");
return result;
}
Added timing to method StringBuilder.buildString
[dennis]$ java StringBuilder 1000 2000 4000 8000 16000

Exception in thread "main" java.lang.VerifyError:
(class: StringBuilder, method: buildString signature:
(I)Ljava/lang/String;) Expecting to find integer on stack

Kiểu kết quả này không phải là một vấn đề miễn là bạn cẩn thận với mã nguồn mà
bạn cung cấp cho Javassist. Tuy nhiên thật quan trọng để nhận ra rằng Javassist sẽ
không nhất thiết phải bắt giữ bất kỳ lỗi nào trong mã này và rằng các kết quả của
một lỗi có thể khó dự đoán.


Nhìn về phía trước
Có rất nhiều thứ với Javassist hơn những gì chúng tôi đã trình bày trong bài viết
này. Tháng tới, chúng ta sẽ nghiên cứu sâu hơn một chút bằng việc xem xét một số
các kết nối đặc biệt mà Javassist cung cấp cho việc sửa đổi số lượng lớn các lớp và
cho việc sửa đổi đang thực hiện như là các lớp được nạp trong thời gian chạy. Đây
là những tính năng cung cấp cho Javassist một công cụ quan trọng để triển khai
thực hiện các khía cạnh trong các ứng dụng của bạn, vì vậy hãy chắc chắn rằng
bạn nắm bắt được các phần tiếp theo với câu chuyện đầy đủ về công cụ mạnh mẽ
này.

Mục lục

 Những điều cơ bản về Javassist
 Hoạt động lớp với Javassist
 Tin tưởng vào mã nguồn, Luke?
 Nhìn về phía trước

×