2009-11-23 7 views
23

Nếu người ta đã tuần tự hóa toàn bộ tệp .class thành byte [], và giả sử tên của lớp được biết (được chuyển cùng với byte []), bạn chuyển đổi byte [] -> Class -> rồi tải nó như thế nào với JVM để sau này tôi có thể sử dụng nó bằng cách gọi Class.forName()?Java: Làm thế nào để nạp Class được lưu trữ dưới dạng byte [] vào JVM?

LƯU Ý: Tôi đang làm điều này vì tôi đã gửi .class sang máy chủ khác và JVM của máy chủ không biết về lớp này .class.

Trả lời

25

Tôi đang thực sự sử dụng một cái gì đó giống như này ngay bây giờ trong một thử nghiệm để cung cấp cho một tập hợp các định nghĩa lớp như byte [] để một ClassLoader:

public static class ByteClassLoader extends URLClassLoader { 
    private final Map<String, byte[]> extraClassDefs; 

    public ByteClassLoader(URL[] urls, ClassLoader parent, Map<String, byte[]> extraClassDefs) { 
     super(urls, parent); 
     this.extraClassDefs = new HashMap<String, byte[]>(extraClassDefs); 
    } 

    @Override 
    protected Class<?> findClass(final String name) throws ClassNotFoundException { 
     byte[] classBytes = this.extraClassDefs.remove(name); 
     if (classBytes != null) { 
     return defineClass(name, classBytes, 0, classBytes.length); 
     } 
     return super.findClass(name); 
    } 

    } 
+0

Alex, nhưng nếu 2 .class phụ thuộc vào nhau thì sao? nó vẫn hoạt động? – sivabudh

+0

Có lẽ là không. Bạn có lẽ nên gửi (ví dụ) một tệp JAR không phải là một chuỗi các tệp .class riêng biệt. –

+3

@ ShaChris23 - chắc chắn, tại sao phải không? @Stephen C - điều đó có vẻ như một cái nhìn giới hạn về những gì mà trình nạp lớp Java có thể làm. Tại sao bạn nói có lẽ không? –

4

Kéo dài ClassLoader và sau đó thực hiện cuộc gọi cần thiết để nhận số byte[] bên trong phương thức defineClass(). Ngược lại, bạn có thể làm điều này trong findClass(). Do đó bạn sẽ tải ClassLoader này ngay từ đầu hoặc sử dụng nó khi bạn cần các định nghĩa lớp thông qua mảng của bạn.

public class ByteArrayClassLoader extends ClassLoader { 

    public Class findClass(String name) { 
     byte[] ba = /* go obtain your byte array by the name */; 

     return defineClass(name,ba,0,ba.length); 
    } 

} 
+0

bạn cũng không cần phải gọi resolveClass()? – sivabudh

9

Sử dụng defineClass (String, byte [], int, int) phương pháp từ ClassLoader

1

Được rồi, vì vậy đây là những gì tôi đã làm:

public class AgentClassLoader extends ClassLoader 
{ 
    public void loadThisClass(ClassByte classByte_) 
    { 
    resolveClass(defineClass(classByte_.getName(), 
          classByte_.getBytes(), 
          0, 
          classByte_.getBytes().length)); 
    } 
} 

Tôi hy vọng rằng tải lớp thành JVM? Nếu điều này thực sự hoạt động, tại sao Java không thực hiện cả hai phương thức công khai defineClass và resolveClass? Tôi thực sự tự hỏi họ đang nghĩ gì. Bất cứ ai cũng có thể khai sáng cho tôi?

Chỉ cần cho đầy đủ, đây là ClassByte

import java.io.Serializable; 

public class ClassByte implements Serializable 
{ 
    private String name; 
    private byte[] bytes; 

    ClassByte(String name_, byte[] bytes_) 
    { 
    name = name_; 
    bytes = bytes_; 
    } 

    public String getName() 
    { 
    return name; 
    } 

    public byte[] getBytes() 
    { 
    return bytes; 
    } 
} 
+1

Tôi nghĩ rằng ý tưởng là để ngăn cản mã ngẫu nhiên gọi 'defineClass' trên các trình nạp lớp hệ thống/mặc định. Điều này có khả năng làm tổn hại đến an ninh và thường làm cho ** những điều thực sự kỳ lạ ** xảy ra. Tốt hơn là chỉ cho phép điều này trên các trình nạp lớp tùy chỉnh. –

0

Câu trả lời của Alex là chính xác nhưng nếu bạn có mối quan hệ thừa kế giữa các lớp bạn cần phải chắc chắn rằng bạn tải lớp cha đầu tiên. Nếu không, trình nạp lớp sẽ không thể xác định nó.

+0

Thật sao? Tôi nghĩ rằng đó là một vấn đề liên kết chứ không phải là một vấn đề định nghĩa lớp. Các lớp không cần phải được định nghĩa theo thứ tự đúng, nếu không chúng ta không thể có các phụ thuộc vòng tròn trong các lớp. Ví dụ, Class.getName() trả về một String nhưng String.getClass() trả về một Class. Hơn nữa, cả hai lớp có một đối tượng của Object nhưng Object.toString() và Object.getClass() trả về String và Class tương ứng. Nếu những gì bạn nói là đúng thì sẽ không có thứ tự đúng để tải các lớp này. – Kidburla

+0

@Kidburla đó là bởi vì JVM không tải cho các lớp khi chúng cần thiết, vì vậy bạn có thể tải Lớp mà không cần tải chuỗi và Chuỗi sẽ được tải khi bạn gọi 'getName'. Tuy nhiên, siêu lớp/giao diện cần phải được nạp với hoặc trước khi lớp được nạp, nếu không lớp không đầy đủ. Đó là lý do tại sao chúng ta không thể có thừa kế tròn, bởi vì các trình nạp lớp không biết cách xử lý điều đó. –

+0

Đúng.Xin lỗi, tôi đã đọc sai "thừa kế" là "phụ thuộc" vì nhận xét về câu trả lời được chấp nhận. – Kidburla