2010-11-09 28 views
11

Liên quan đến: Is there a way to obtain the bytecode for a class at runtime?Java: Bắt Bytecode của Class tại Runtime từ bên trong JVM Cùng

tôi thêm độ bền để Clojure, và tôi cuối cùng tại điểm mà tôi đã sẵn sàng để thêm chức năng. Trong Clojure, các hàm được biên dịch thành các lớp với các phương thức gọi (trong số các hàm khác). Bằng cách này, các hàm là lớp đầu tiên. Để làm cho độ bền cao, tôi cần phải tuần tự hóa và deserialize các lớp này. Làm thế nào để có được bytecode cho lớp mà không có quyền truy cập vào tệp .class?

Vui lòng sửa tôi nếu tôi nhầm, nhưng sử dụng tác nhân yêu cầu sinh ra một máy ảo riêng biệt với tác nhân kết nối với máy ảo đầu tiên. Tôi cần phải làm điều đó từ cùng một máy ảo.

Không đủ để sử dụng Serializable để đặt và nhận đối tượng Lớp. Sau khi deserializing, tôi cần phải tải lớp, và sau khi trường hợp VM tiếp theo, có thể có một tên lớp va chạm. Tôi cần phải sửa đổi bytecode để đổi tên lớp thành một cái gì đó duy nhất tại thời gian deserialization/class-load.

+0

Tôi không phải là chuyên gia về chủ đề này, nhưng có thể cố gắng duy trì chức năng * định nghĩa * chứ không phải là bytecode cơ bản. sau đó bạn có thể biên dịch lại các hàm thành bytecode khi bạn nạp chúng trở lại. – mikera

Trả lời

4

Bạn có thể tự viết ClassLoader của mình và tấn công một sơ đồ ghi lại bytecode khi các lớp được nạp.

Bạn cần ghi đè findClass để tự tìm tệp lớp học, tải tệp đó vào bộ nhớ, lưu dữ liệu ở đâu đó (để tuần tự hóa sau), sau đó gọi defineClass để xác định lớp đó trong JVM.

+0

p.s. Tôi nghĩ rằng các đại lý chạy trong cùng một máy ảo như chương trình chính, vì vậy họ có thể làm việc cho bạn. –

+0

Javassist đã triển khai sơ đồ này và cung cấp quyền truy cập phần nào thuận tiện cho bytecode được đề cập. BCEL cũng có thể làm điều tương tự. – Blaisorblade

+0

Việc triển khai tối thiểu là khá nhỏ: https://gist.github.com/Felk/ed4375d27c755e21d0e6893847286d93 (Tôi biết bài đăng này là 7 tuổi) – Felk

3

Trừ khi bạn đang chạy mã thông qua một classloader khéo léo, bạn sẽ có thể làm điều gì đó như thế này:

Class<?> clazz = .... 
String className = clazz.getCanonicalName(); // e.g. "foo.Bar" 
String resourceName = ... // map className to a resource name; e.g. "/foo/Bar.class" 
InputStream is = clazz.getClassLoader.getResourceAsStream(resourceName); 

này mang đến cho bạn một ngày xử lý các nội dung của tập tin ".class" ... nếu Nó có thể được tìm thấy.

Cẩn thận. Một số classloaders thể:

  • không để để mở ".class" nguồn lực ở tất cả,
  • cung cấp cho bạn một dòng bytecode mã hóa, hoặc
  • cung cấp cho bạn bytecode mà cũng không được chạy là chính xác những gì, do cho một số chuyển đổi trực tiếp được thực hiện bởi trình nạp lớp.

Nếu cách tiếp cận này không hoạt động, bạn có khá nhiều lựa chọn vì JVM không cung cấp cách truy cập các mã byte thực tế đã được tải.

+1

Than ôi, điều này chỉ hoạt động nếu lớp .class được ghi vào đĩa làm tài nguyên ở vị trí đầu tiên. Clojure không làm điều này. – alyssackwan

+0

Nó cũng có vấn đề khi lớp được định nghĩa bên trong lớp khác. Ví dụ: 'class Foo {class Bar {}}' sẽ trở thành "/Foo$Bar.class" – iliis

+1

'clazz.getResourceAsStream ('/' + clazz.getName(). Replace ('.', '/') + ".class") 'làm việc với các lớp bên trong và thậm chí với các lớp bootstrap có một lớp' null' ClassLoader. – Holger

1

Bạn cũng có thể sử dụng API thiết bị đo Java cho việc này. Bạn nhận được quyền truy cập vào các byte của classfile trước khi defineClass được gọi. Bạn cũng có thể thay đổi chúng!

Các vấn đề liên quan