2014-11-30 14 views
9

Tôi có hai lớp (AB) được tải bởi ClassLoaders khác nhau. Hơn nữa, tôi có một lớp thứ ba, cung cấp các phương thức getter và setter tĩnh. Tôi hy vọng sau bức tranh có thể làm rõ tình hình:Truy cập phương pháp tĩnh từ các lớp được nạp bởi các Trình nạp lớp khác nhau

enter image description here

Lớp Data trông như sau:

public class Data { 

    private static String data = "<fill in>"; 

    public static void setData(String d) { 
     data = d; 
    } 

    public static String getData() { 
     return data; 
    } 
} 

Trong lớp A, tôi muốn thiết lập giá trị tĩnh của Data và trong B Tôi muốn để lấy giá trị này. Tuy nhiên, trong B tôi luôn nhận được giá trị ban đầu (là "<fill in>"). Tôi chỉ có một sự hiểu biết cơ bản của ClassLoader s, vì vậy tôi không quá chắc chắn những gì đang xảy ra dưới mui xe. Tôi nghĩ rằng cả hai ClassLoaders (clAclB) sẽ truyền cho phụ huynh của họ ClassLoader và rằng tôi sẽ nhận được cùng một lớp Data trong cả hai. Bất cứ ai có thể cho tôi một số thông tin phản hồi về hành vi hoặc chỉ cho tôi theo hướng để xem xét?

Cập nhật

Khi tôi in hashCode() của cả hai Data lớp, tôi nhận được giá trị khác nhau cho họ (có nghĩa là rõ ràng là tôi không nhận được truy cập cùng lớp). Có cách nào và dễ dàng để minh họa cho phân cấp ClassLoader?

+3

Bạn có chắc chắn rằng cả hai 'lớp A' và' lớp B' đang nói chuyện với "cùng" lớp 'Data' như trong - lớp' Data' được nạp bởi một trình nạp lớp đơn? Nếu 'Data' đang được nạp bởi các trình nạp lớp khác nhau và' lớp A' và 'lớp B' đang nói đến các phiên bản khác nhau như vậy, thì những gì bạn đang thấy được mong đợi. Nó phụ thuộc rất nhiều vào hệ thống phân cấp bộ nạp lớp, vì vậy một chút bối cảnh đó sẽ giúp ích. – mystarrocks

+0

@mystarrocks cảm ơn vì phản hồi, điều đó đã giúp ích. Có vẻ như tôi thực sự không có cùng tham chiếu lớp học. Tôi đã cập nhật câu hỏi của mình cho phù hợp. Cám ơn! – WeSt

+0

Các lớp này có thuộc về một ứng dụng đang chạy trên máy chủ không? Các vùng chứa khác nhau sử dụng các kỹ thuật xếp lớp khác nhau. – mystarrocks

Trả lời

2

Nếu câu hỏi của bạn là cách minh họa hoặc hình dung thứ bậc của trình nạp lớp cho các đối tượng, thì bạn có thể đi lên từng lớp trình nạp lớp trong mã. Bạn nói rằng bạn đang sử dụng groovy, vì vậy một ví dụ sẽ trông như thế:

def showObjectClassLoaderHierarchy(Object obj) { 
    def classLoader = showClassLoaderHierarchy(obj.getClass().getClassLoader()); 
    showClassLoaderHierarchy(classLoader); 
} 

def showClassLoaderHierarchy(ClassLoader loader) { 

    if (loader != null) { 
     println "Classloader: " + loader.hashCode(); 
     while (loader.getParent() != null) { 
       loader = loader.getParent(); 
      println " Child of: " + loader.hashCode(); 
     } 
    } 

} 

Tôi nghĩ rằng bạn sẽ tìm thấy, trong mã của bạn, hai đối tượng dữ liệu đang thực sự không được nạp từ classloader cùng, đó là lý do họ có các biến tĩnh khác nhau.

Tôi đặt cùng một mẫu có

  • Main (nạp từ classloader mẹ)
  • DataObj với một static String (nạp cũng từ classloader mẹ)
  • LoadA, mà khởi tạo một bản sao của DataObj (được tải từ bộ nạp lớp con A)
  • TảiB, tức là bản sao của DataObj (được tải từ bộ nạp lớp con B)

Tôi thấy rằng trong khi LoadA và LoadB có các trình nạp lớp khác nhau, thì DataObj và biến tĩnh đến từ một trình nạp lớp phổ biến.

Toàn mã tại địa chỉ: https://github.com/lucasmcgregor/groovy_classloader_test

Đối tượng chính trong groovy:

import java.lang.ClassLoader; 
import java.net.URLClassLoader; 
import java.net.URL; 

def showObjectClassLoaderHierarchy(Object obj) { 
     def classLoader = showClassLoaderHierarchy(obj.getClass().getClassLoader()); 
     showClassLoaderHierarchy(classLoader); 
} 

def showClassLoaderHierarchy(ClassLoader loader) { 

     if (loader != null) { 
      println "Classloader: " + loader.hashCode(); 
      while (loader.getParent() != null) { 
        loader = loader.getParent(); 
        println " Child of: " + loader.hashCode(); 
      } 
     } 

} 

println "Setting up child classLoaders A and B..."; 

def URL[] urlsA = [new URL("file:///tmp/cla/")]; 
def classLoaderA = new URLClassLoader(urlsA, this.getClass().getClassLoader()); 

def URL[] urlsB = [new URL("file:///tmp/clb/")]; 
def classLoaderB = new URLClassLoader(urlsB, this.getClass().getClassLoader()); 


println "Classloader A heirachry:"; 
showClassLoaderHierarchy(classLoaderA); 

println "Classloader B: "; 
showClassLoaderHierarchy(classLoaderB); 

println ""; 
println "Now loading Load classes A and B from seperate classloaders:"; 
def loadA = classLoaderA.loadClass("LoadA").newInstance(); 
def loadB = classLoaderB.loadClass("LoadB").newInstance(); 

print "LoadA: heirachry"; 
showObjectClassLoaderHierarchy(loadA); 
print "LoadB: heirachry"; 
showObjectClassLoaderHierarchy(loadB); 

println ""; 
println "Now pulling the data objects from both and comparing classloders and static data: "; 
def dobjA = loadA.getDataObj(); 
def dobjB = loadB.getDataObj(); 

println "dataA static field:" + dobjA.getData(); 
println "dataA static field hashcode: " + dobjA.getData().hashCode(); 
println "dataA hashcode: " + dobjA.hashCode(); 
println "dataA classloader: "; 
showObjectClassLoaderHierarchy(dobjA); 

println "dataB static field: " + dobjB.getData(); 
println "dataB static field hashcode: " + dobjB.getData().hashCode(); 
println "dataB hashcode: " + dobjB.hashCode(); 
println "dataB classLoader:"; 
showObjectClassLoaderHierarchy(dobjB); 

Kết quả là:

Setting up child classLoaders A and B... 
Classloader A heirachry: 
Classloader: 1926764753 
    Child of: 1163157884 
    Child of: 1022308509 
Classloader B: 
Classloader: 846238611 
    Child of: 1163157884 
    Child of: 1022308509 

Now loading Load classes A and B from seperate classloaders: 
LoadA: heirachryClassloader: 1926764753 
    Child of: 1163157884 
    Child of: 1022308509 
LoadB: heirachryClassloader: 846238611 
    Child of: 1163157884 
    Child of: 1022308509 

Now pulling the data objects from both and comparing classloders and static data: 
dataA static field:Loaded By B 
dataA static field hashcode: 1828548084 
dataA hashcode: 2083117811 
dataA classloader: 
Classloader: 1163157884 
    Child of: 1022308509 
dataB static field: Loaded By B 
dataB static field hashcode: 1828548084 
dataB hashcode: 157683534 
dataB classLoader: 
Classloader: 1163157884 
    Child of: 1022308509 

Bạn thấy rằng LoadA và LoadB cả hai đều có classloaders khác nhau, nhưng họ chia sẻ trình nạp lớp cha.

Trình nạp lớp cấp độ gốc tải DataObj cho cả hai trường hợp của LoadA.dataObj và LoadB.dataObj.

LoadA.dataObj và LoadB.dataObj có các mã băm khác nhau.

Tuy nhiên, LoadA.dataObj.data và LoadB.dataObj.data có cùng mã băm vì đây là đối tượng tĩnh. Họ cũng có cùng giá trị. LoadB instantiates dữ liệu của nóObj cuối cùng và đặt chuỗi thành "Loaded By B"

1

Tôi nghĩ Lucas thực sự đã trả lời câu hỏi của bạn để minh họa cấu trúc phân cấp. Tôi chỉ muốn thêm câu trả lời của mình để làm sáng tỏ một số lời nhắc về câu hỏi

Trong Java cặp (xác định trình nạp lớp, tên lớp) là duy nhất. Định nghĩa trình nạp lớp ở đây có nghĩa là trình nạp, mà thực sự nhận ra lớp là lớp từ bytecode. Tính duy nhất này có nghĩa là một trình nạp lớp xác định lớp X không thể định nghĩa một lớp X thứ hai, nó phải có một tên khác. Nhưng một trình nạp lớp khác có thể định nghĩa lớp đó. ClassLoaders được cấu trúc trong một loại cây (nó không thực sự là một DAG, nhưng điều đó đi xa đến đây) và một trình nạp lớp có nghĩa vụ phải hỏi cha mẹ của nó trước tiên nếu được truy vấn cho một lớp. Vì vậy, nó có thể xảy ra, rằng dữ liệu tồn tại hai lần, ví dụ một lần trong CIA và một lần trong CIB. Để tránh điều này, bạn thường muốn có dữ liệu được định nghĩa bởi một trình nạp lớp (classloader) mà là một phụ huynh để cIA và cIB. Đó là giả định hai bộ tải hoạt động theo các ràng buộc của trình nạp lớp, như yêu cầu cha mẹ trước.

Vì đây cũng là một kịch bản Groovy, nhưng không có chi tiết thực sự về thiết lập giả thiết của tôi là ciB không có cha mẹ biết dữ liệu và thư viện được đưa vào url của GroovyClassLoader được sử dụng và bạn đã sử dụng GroovyShell. Thay vào đó GroovyShell được khởi tạo với một trong các trình nạp lớp lấy các đối số và trình nạp lớp này là một con để trình nạp định nghĩa dữ liệu, cũng là một cha mẹ để cIA (cha mẹ có thể là trình nạp tương tự trong mọi trường hợp tôi đã sử dụng cụm từ mẹ).

Cảnh báo ... GroovyClassLoader không phải là trình nạp lớp hoạt động tốt. Nó sẽ định nghĩa trước các lớp (ví dụ thông qua một kịch bản) qua các lớp từ lớp cha. Nếu bạn có một kịch bản với lớp dữ liệu trong nó, nó sẽ sử dụng lớp dữ liệu đó, ngay cả khi cha mẹ thường là trình nạp lớp xác định

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