1. Trang chủ
  2. » Công Nghệ Thông Tin

Động lực học lập trình Java, Phần 7: Kỹ thuật bytecode với BCEL Apache BCEL cho phép bạn đi đến các chi tiết về ngôn ngữ assembler của JVM cho hoạt động lớp doc

24 360 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 24
Dung lượng 276,94 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Bạn có thể sử dụng nó để biến đổi các biểu diễn lớp hiện tại hoặc xây dựng các lớp mới, và vì BCEL làm việc ở mức các lệnh JVM riêng biệt, nó sẽ cho bạn sức mạnh tối đa trên mã của bạn..

Trang 1

Động lực học lập trình Java, Phần 7: Kỹ thuật bytecode với BCEL

Apache BCEL cho phép bạn đi đến các chi tiết về ngôn ngữ assembler của JVM cho hoạt động lớp

Dennis Sosnoski, Nhà tư vấn, Sosnoski Software Solutions, Inc

Tóm tắt: Apache Byte Code Engineering Library (BCEL-Thư viện kỹ thuật mã

byte) cho phép bạn nghiên cứu bytecode của các lớp Java Bạn có thể sử dụng nó

để biến đổi các biểu diễn lớp hiện tại hoặc xây dựng các lớp mới, và vì BCEL làm việc ở mức các lệnh JVM riêng biệt, nó sẽ cho bạn sức mạnh tối đa trên mã của bạn Mặc dù sức mạnh đó đi kèm với một chi phí về độ phức tạp Trong bài này, nhà tư vấn Java Dennis Sosnoski cung cấp cho bạn những điều cơ bản về BCEL

và hướng dẫn bạn thông qua một ví dụ ứng dụng BCEL để cho bạn có thể tự quyết định xem sức mạnh có tương xứng với sự phức tạp không

Trong ba bài viết mới đây của loạt bài này, tôi đã cho bạn thấy cách sử dụng

khung công tác Javassist cho hoạt động lớp (classworking) Bây giờ tôi sẽ trình bày một cách tiếp cận rất khác để xử lí bytecode, đó là sử dụng Apache Byte Code Engineering Library (BCEL) BCEL hoạt động ở mức các lệnh JVM thực sự, không giống như giao diện mã nguồn được Javassist hỗ trợ Cách tiếp cận mức thấp làm cho BCEL rất tốt khi bạn thực sự muốn kiểm soát mọi bước thực hiện chương trình, nhưng nó cũng làm cho hoạt động với BCEL phức tạp hơn nhiều so với sử dụng Javassist cho các trường hợp ở đó cả hai cùng làm việc

Tôi sẽ bắt đầu bằng cách trình bày kiến trúc BCEL cơ bản, sau đó dành hầu hết bài viết này cho việc xây dựng lại ví dụ hoạt động lớp Javassist đầu tiên của tôi bằng BCEL Tôi sẽ kết thúc bằng việc xem xét một số các công cụ có trong gói BCEL

và một vài ứng dụng mà các nhà phát triển đã xây dựng ở trên BCEL

Truy cập lớp BCEL

BCEL cung cấp cho bạn tất cả các khả năng cơ bản giống như Javassist kiểm tra, chỉnh sửa và tạo các lớp Java nhị phân Sự khác biệt rõ ràng với BCEL là mọi thứ được thiết kế để làm việc ở mức ngôn ngữ chương trình dịch hợp ngữ (assembler) của JVM, hơn là giao diện mã nguồn do Javassist cung cấp Có một số khác biệt sâu hơn dưới các vỏ bọc, gồm việc sử dụng hai hệ thống phân cấp riêng của các thành phần trong BCEL một cái kiểm tra mã hiện có và cái khác để tạo mã mới Tôi sẽ giả định bạn quen với Javassist từ những bài viết trước trong loạt bài này (xem phần bên cạnh Đừng bỏ lỡ phần còn lại của loạt bài này) Vì vậy tôi sẽ tập trung vào những sự khác biệt có khả năng gây nhầm lẫn cho bạn khi bạn bắt đầu làm việc với BCEL

Trang 2

Như với Javassist, các khía cạnh kiểm tra lớp của BCEL về cơ bản lặp lại những gì

có sẵn trực tiếp trong nền tảng Java qua Reflection API Điều trùng lắp này là cần thiết trong một bộ công cụ hoạt động lớp vì bạn thường không muốn nạp các lớp

mà bạn đang làm việc với chúng cho đến sau khi chúng đã được thay đổi

Đừ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 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 8, "Thay thế sự phản chiếu bằng việc tạo mã" (06.2004)

BCEL cung cấp một số định nghĩa không thay đổi cơ bản trong gói

org.apache.bcel, nhưng không kể những định nghĩa này, tất cả các mã kiểm tra có liên quan nằm trong gói org.apache.bcel.classfile Điểm khởi đầu trong gói này là lớp JavaClass Lớp này đóng vai trò giống như trong việc truy cập thông tin lớp bằng cách sử dụng BCEL như lớp java.lang.Class thực hiện khi dùng sự phản chiếu của Java chuẩn JavaClass xác định các phương thức để nhận được thông tin trường và phương thức cho lớp này, cũng như thông tin theo cấu trúc về siêu lớp

và các giao diện Không giống như java.lang.Class, JavaClass cũng cung cấp quyền truy cập tới các thông tin nội bộ cho lớp đó, gồm nhóm hằng số và các thuộc tính và biểu diễn lớp nhị phân đầy đủ như một luồng byte

Các cá thể JavaClass thường được tạo bằng cách phân tích cú pháp lớp nhị phân hiện có BCEL cung cấp lớp org.apache.bcel.Repositoryđể thực hiện phân tích cú pháp cho bạn Theo mặc định, BCEL phân tích cú pháp và lưu trữ nhanh các biểu diễn của các lớp được tìm thấy trong đường dẫn lớp (classpath) JVM, nhận được các biểu diễn lớp nhị phân thực sự từ một cá thể org.apache.bcel.util.Repository (lưu ý sự khác biệt trong tên gói) Hiện tại org.apache.bcel.util.Repository là một giao diện cho một nguồn biểu diễn lớp nhị phân Bạn có thể thay thế các đường dẫn khác để tìm kiếm các tệp lớp hoặc các cách truy cập thông tin lớp khác, thay cho nguồn mặc định có sử dụng đường dẫn lớp

Thay đổi các lớp

Trang 3

Bên cạnh việc truy cập kiểu-phản chiếu tới các thành phần lớp,

org.apache.bcel.classfile.JavaClass cũng cung cấp các phương thức để thay đổi lớp

đó Bạn có thể sử dụng những phương thức này để thiết lập bất kỳ các thành phần lớp này với các giá trị mới Mặc dù, chúng thường không sử dụng trực tiếp, vì các lớp khác trong gói đó không hỗ trợ cho việc xây dựng các phiên bản mới của các thành phần theo bất kỳ cách hợp lý nào Thay vào đó, có một tập riêng biệt đầy đủ các lớp trong gói org.apache.bcel.generic để cung cấp phiên bản có thể chỉnh sửa được của các thành phần được các lớp org.apache.bcel.classfile biểu diễn

Cũng như org.apache.bcel.classfile.JavaClass là điểm khởi đầu cho việc sử dụng BCEL để kiểm tra các lớp hiện có, org.apache.bcel.generic.ClassGen là điểm bắt đầu của bạn để tạo các lớp mới Nó cũng thay đổi các lớp hiện tại để xử lý trường hợp đó, có một hàm tạo lấy một cá thể JavaClass và sử dụng nó để khởi tạo thông tin lớp ClassGen Một khi bạn đã hoàn tất các thay đổi lớp của bạn, bạn có thể nhận được một sự biểu diễn lớp thích hợp từ cá thể ClassGen bằng cách gọi một phương thức trả về một JavaClass, nó có thể lần lượt được chuyển đổi sang biểu diễn lớp nhị phâ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

Nói lộn xộn quá phải không? Tôi nghĩ như vậy Trong thực tế, việc quay lại và tiến lên giữa hai gói này là một trong những khía cạnh làm việc bất tiện nhất với BCEL Các cấu trúc lớp sao chép lại hướng theo cách này, vì thế nếu bạn đang làm việc với BCEL, thật bõ công để viết các lớp của trình bao bọc (wrapper), mà nó có thể ẩn dấu một số các sự khác nhau này Với bài viết này, tôi sẽ chủ yếu làm việc với các lớp của gói org.apache.bcel.generic và tránh sử dụng các trình bao bọc, nhưng với bạn đây là một điều để ghi nhớ cho công việc riêng của mình

Ngoài ClassGen, gói org.apache.bcel.generic định nghĩa các lớp để quản lý việc xây dựng các thành phần lớp khác nhau Các lớp xây dựng này gồm

ConstantPoolGen để xử lý nhóm hằng số FieldGen và MethodGen cho các trường

và các phương thức và InstructionList để làm việc với các chuỗi của các lệnh JVM Cuối cùng, gói org.apache.bcel.generic cũng định nghĩa các lớp để biểu diễn mọi kiểu của các lệnh JVM Bạn có thể trực tiếp tạo các cá thể của các lớp này hoặc trong một số trường hợp bằng cách sử dụng lớp của trình trợ giúp (helper) org.apache.bcel.generic.InstructionFactory Lợi thế của việc sử dụng

InstructionFactory là nó xử lý các chi tiết tạo sổ sách của việc xây dựng lệnh cho

Trang 4

bạn (gồm cả việc thêm các mục vào nhóm hằng số khi cần thiết cho các lệnh) Bạn

sẽ thấy cách làm cho tất cả các lớp này hoạt động cùng nhau trong phần tiếp theo

Hoạt động lớp với BCEL

Đối với một ví dụ về việc áp dụng BCEL, tôi sẽ sử dụng cùng một nhiệm vụ mà tôi đã sử dụng như một ví dụ Javassist trong Phần 4 việc đo thời gian được dùng

để thực hiện một phương thức Tôi thậm chí sẽ sử dụng cùng một cách tiếp cận mà tôi đã sử dụng với Javassist: tôi sẽ tạo một bản sao của phương thức ban đầu có tính thời gian khi sử dụng một tên đã thay đổi, sau đó thay thế phần thân của

phương thức ban đầu với mã bao bọc các tính toán đếm thời gian xung quanh một cuộc gọi đến phương thức đã đổi tên

Chọn một vật thí nghiệm

Liệt kê 1 đưa ra một phương thức ví dụ mà tôi sẽ sử dụng cho các mục đích giải thích: phương thức buildString của lớp StringBuilder Như tôi đã nói trong Phần 4, phương thức này xây dựng một String có độ dài yêu cầu 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 khuyên bạn

không nên làm nó liên tục gắn thêm một ký tự vào cuối của một chuỗi để tạo

một chuỗi dài hơn Vì các chuỗi không thay đổi được, nên 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 khi qua vòng lặp, với các dữ liệu được sao chép từ chuỗi cũ và một ký tự được thêm vào cuối Ảnh hưởng cuối cùng

là phương thức này phải chịu 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) {

Trang 5

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 " +

Liệt kê 2 Tính thời gian đã thêm vào phương thức ban đầu

Trang 6

public class StringBuilder

{

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$impl took " + (System.currentTimeMillis()-start) + " ms."); return result;

}

public static void main(String[] argv) {

StringBuilder inst = new StringBuilder();

Trang 7

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());

}

}

}

Mã hóa phép chuyển đổi

Triển khai thực hiện mã để thêm việc tính thời gian phương thức sử dụng các BCEL API mà tôi đã nêu ra trong phần Truy cập lớp BCEL Làm việc ở mức các lệnh JVM làm cho đoạn mã dài hơn rất nhiều so với ví dụ Javassist trong Phần 4,

do đó ở đây tôi sẽ duyệt qua nó từng đoạn một trước khi cung cấp cho bạn việc thực hiện đầy đủ Trong đoạn mã cuối cùng, tất cả các đoạn này sẽ tạo nên chỉ một phương thức, một phương thức lấy một cặp tham số: cgen, một cá thể của lớp org.apache.bcel.generic.ClassGen được khởi tạo bằng các thông tin hiện có cho các lớp đang được thay đổi; và method (phương thức), một cá thể

org.apache.bcel.classfile.Method cho phương thức tôi sắp tính thời gian

Liệt kê 3 có đoạn mã đầu tiên cho phương thức chuyển đổi Như bạn thấy từ các ý kiến, phần đầu tiên chỉ khởi tạo các thành phần BCEL cơ bản mà tôi sắp sử dụng, gồm việc khởi tạo một cá thể org.apache.bcel.generic.MethodGen mới bằng cách

sử dụng các thông tin cho phương thức có tính giờ Tôi thiết lập một danh sách lệnh rỗng cho MethodGen, để sau này tôi sẽ điền vào đó với mã tính thời gian thực

tế Trong phần thứ hai, tôi tạo một cá thể org.apache.bcel.generic.MethodGen thứ hai từ phương thức ban đầu, sau đó loại bỏ phương thức ban đầu khỏi lớp đó Trong cá thể MethodGen thứ hai này, tôi chỉ cần thay đổi tên để sử dụng một hậu

tố "$impl", sau đó gọi phương thức getMethod() để chuyển đổi thông tin phương thức có thể thay đổi được thành một dạng cố định như một cá thể

org.apache.bcel.classfile.Method Sau đó tôi sử dụng cuộc gọi addMethod() để thêm phương thức đã đổi tên vào lớp đó

Trang 8

Liệt kê 3 Thêm phương thức chặn

// set up the construction tools

InstructionFactory ifact = new InstructionFactory(cgen);

InstructionList ilist = new InstructionList();

ConstantPoolGen pgen = cgen.getConstantPool();

String cname = cgen.getClassName();

MethodGen wrapgen = new MethodGen(method, cname, pgen);

wrapgen.setInstructionList(ilist);

// rename a copy of the original method

MethodGen methgen = new MethodGen(method, cname, pgen);

Trang 9

java.lang.System.currentTimeMillis() để nhận được thời gian bắt đầu, lưu nó vào khoảng trống của biến cục bộ đã được tính toán trong khung ngăn xếp

Có lẽ bạn tự hỏi tại sao tôi kiểm tra xem phương thức đó có tĩnh hay không ở lúc bắt đầu tính toán kích thước tham số của tôi, sau đó khởi tạo khe hở khung ngăn xếp là 0 nếu nó có (trái ngược với một khe hở nếu nó không có) Cách tiếp cận này liên quan đến cách xử lý các cuộc gọi phương thức ngôn ngữ Java Đối với các phương thức không tĩnh, tham số (ẩn) đầu tiên trên mỗi cuộc gọi là tham chiếu này cho đối tượng đích, mà tôi cần phải tính đến khi tính toán kích thước tập tham số đầy đủ trên khung ngăn xếp

Liệt kê 4 Thiết lập cho cuộc gọi được bao bọc

// compute the size of the calling parameters

Type[] types = methgen.getArgumentTypes();

int slot = methgen.isStatic() ? 0 : 1;

for (int i = 0; i < types.length; i++) {

Trang 10

Liệt kê 5 cho thấy đoạn mã để tạo cuộc gọi đến phương thức được bao bọc và lưu kết quả (nếu có) Phần đầu tiên của đoạn mã này sẽ kiểm tra xem phương thức này

có tĩnh không Nếu phương thức không tĩnh, tôi tạo mã để nạp tài liệu tham khảo đối tượng này cho ngăn xếp đó và cũng có thể thiết lập kiểu gọi phương thức là ảo (virtual) (chứ không phải tĩnh (static)) Vòng lặp for sau đó tạo mã để sao chép tất

cả các giá trị tham số cuộc gọi tới ngăn xếp đó, phương thức createInvoke() tạo cuộc gọi thực sự tới phương thức được bao bọc và câu lệnh ifcuối cùng sẽ lưu giá trị kết quả đến vị trí biến cục bộ khác trong khung ngăn xếp (nếu kiểu kết quả không phải là rỗng )

Liệt kê 5 Gọi phương thức được bao bọc

// call the wrapped method

for (int i = 0; i < types.length; i++) {

Type type = types[i];

Trang 11

java.lang.System.out) và một vài kiểu lệnh khác nhau Hầu hết trong số các kiểu lệnh này nên dễ hiểu nếu bạn nghĩ về JVM như là một bộ xử lý dựa vào-ngăn xếp,

vì vậy tôi sẽ không đi vào chi tiết tại đây

Liệt kê 6 Tính và in ra thời gian đã sử dụng

// print time required for method call

Trang 12

ilist.append(new PUSH(pgen, text));

Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL));

Sau khi mã thông báo tính thời gian được tạo, tất cả công việc để lại cho Liệt kê 7

là hoàn thành mã phương thức của trình bao bọc (wrapper) với một sự trả về giá trị kết quả đã lưu trữ (nếu có) từ cuộc gọi phương thức được bao bọc, tiếp theo là hoàn thành phương thức của trình bao bọc đã được xây dựng Phần cuối cùng này liên quan đến một vài bước Cuộc gọi đến stripAttributes(true) chỉ ra lệnh cho BCEL không tạo các thông tin gỡ rối cho phương thức được xây dựng, trong khi các cuộc gọi setMaxStack() và setMaxLocals() tính toán và thiết lập thông tin về cách sử dụng ngăn xếp cho phương thức này Sau khi việc đó được thực hiện, tôi thực sự có thể tạo phiên bản hoàn chỉnh của phương thức này và thêm nó vào lớp

Liệt kê 7 Hoàn thành trình bao bọc

Ngày đăng: 07/08/2014, 10:22

HÌNH ẢNH LIÊN QUAN

Hình 1 là một ảnh chụp màn hình của kết quả bố trí được trình phân tách tạo nên.  Trong ảnh chụp này khung lớn ở phía trên bên phải cho thấy sự phân tách của  phương thức trình bao bọc tính thời gian được thêm vào lớp StringBuilder - Động lực học lập trình Java, Phần 7: Kỹ thuật bytecode với BCEL Apache BCEL cho phép bạn đi đến các chi tiết về ngôn ngữ assembler của JVM cho hoạt động lớp doc
Hình 1 là một ảnh chụp màn hình của kết quả bố trí được trình phân tách tạo nên. Trong ảnh chụp này khung lớn ở phía trên bên phải cho thấy sự phân tách của phương thức trình bao bọc tính thời gian được thêm vào lớp StringBuilder (Trang 22)

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w