2014-11-30 3 views
9

Ho due classi (A e B) caricate da diversi ClassLoaders. Inoltre, ho una terza classe, che fornisce metodi getter e setter statici. Spero seguente immagine può chiarire la situazione:Metodo statico di accesso da classi caricate da diversi ClassLoaders

enter image description here

La classe Data appare come segue:

public class Data { 

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

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

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

In classe A, voglio impostare il valore statico di Data e B voglio per recuperare questo valore. Tuttavia, in B ottengo sempre il valore originale (che è "<fill in>"). Ho solo una conoscenza di base di ClassLoader s, quindi non sono troppo sicuro di cosa sta succedendo sotto il cofano. Ho pensato che entrambi i ClassLoaders (clA e clB) si propagheranno ai loro genitori ClassLoader e che otterrò la stessa classe Data in entrambi. Qualcuno può darmi qualche feedback sul comportamento o indicarmi la direzione da guardare?

Aggiornamento

Quando stampo il hashCode() di entrambe le classi Data, ottengo valori diversi per loro (che significa, ovviamente, non ho ricevuto accedere alla stessa classe). C'è un modo semplice per illustrare la gerarchia ClassLoader?

+3

Sei sicuro che sia la 'classe A' sia la' classe B' stanno parlando al "same" 'Data' class as in - la classe' Data' caricata da un singolo classloader? Se 'Data' viene caricato da diversi programmi di caricamento classi e' classe A' e 'classe B' stanno parlando con diverse versioni di questo tipo, allora quello che vedi è previsto. Dipende molto dalla gerarchia del classloader, quindi un po 'di quel contesto ti aiuterà. – mystarrocks

+0

@mystarrocks grazie per il feedback, che ha già aiutato. Sembra che davvero non ottenga lo stesso riferimento di classe. Ho aggiornato la mia domanda di conseguenza. Grazie! – WeSt

+0

Queste classi appartengono a un'applicazione in esecuzione su un server? Diversi contenitori utilizzano diverse tecniche di classloading. – mystarrocks

risposta

2

Se la domanda è come illustrare o visualizzare la gerarchia del classloader per gli oggetti, è possibile risalire a ogni classloader di classi nel codice. Lei ha detto che si sta utilizzando Groovy, quindi un esempio potrebbe essere simile:

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

} 

Penso che troverete, nel codice, i due oggetti di dati sono in realtà non caricati dallo stesso classloader, che è per questo che hanno diverse variabili statiche.

ho messo insieme un campione che ha

  • principale (caricato dal classloader genitore)
  • DataObj con una stringa statica (caricato anche dal caricamento classe genitore)
  • Loada, che crea un'istanza di una copia di DataObj (caricati da bambino classloader a)
  • LoadB, che crea un'istanza di una copia di DataObj (caricato dal bambino classloader B)

Vedo che mentre LoadA e LoadB hanno caricatori di classi diversi, DataObj e la variabile statica provengono da un classloader comune.

codice completo a: https://github.com/lucasmcgregor/groovy_classloader_test

l'oggetto principale in 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); 

I risultati sono:

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 

Si vede che Loada e LoadB entrambi hanno diverse classloader, ma condividi un classloader genitore.

Il programma di caricamento classe genitore carica DataObj per entrambe le istanze di LoadA.dataObj e LoadB.dataObj.

LoadA.dataObj e LoadB.dataObj hanno hash diversi.

Tuttavia, LoadA.dataObj.data e LoadB.dataObj.data hanno lo stesso codice hash, poiché questo è l'oggetto statico. Hanno anche lo stesso valore. LoadB crea l'istante di dataObj e imposta la stringa su "Caricato da B"

1

Penso che Lucas abbia effettivamente risposto alla domanda per illustrare la gerarchia. Voglio aggiungere la mia risposta solo per chiarire alcuni promemoria della domanda

In Java la coppia (che definisce classloader, nome classe) è unica. Definire il classloader qui significa il loader, che in realtà realizza la classe come classe dal bytecode. Questa unicità significa che un classloader che definisce la classe X non può definire una seconda classe X, deve avere un nome diverso. Ma un altro classloader può definire la classe. I ClassLoaders sono strutturati in un tipo di albero (non è in realtà un DAG, ma qui va molto lontano) e un classloader dovrebbe chiedere prima ai suoi genitori se interrogati per una classe. Quindi può succedere che Data esista due volte, per esempio una volta in CIA e una volta in CIB. Per evitare ciò, in genere si desidera che i dati vengano definiti da un classloader che è padre di cIA e cIB. Ciò presuppone che i due caricatori si comportino in base ai vincoli del classloader, come chiedere prima ai genitori.

Poiché si tratta anche di uno script Groovy, ma non ci sono dettagli reali forniti sull'impostazione, la mia ipotesi è che ciB non abbia un genitore che conosca Data e che la libreria sia stata data all'URL di GroovyClassLoader utilizzato e che hai usato GroovyShell. Quello che dovrebbe essere fatto è che GroovyShell è istanziato con uno dei classloader che accetta argomenti e che questo classloader è un figlio per il caricatore che definisce Data, che è anche un genitore di cIA (genitore può essere lo stesso loader in tutti i casi in cui ho usato il termine genitore).

Un avviso ... GroovyClassLoader non è un caricatore di classi che funziona correttamente. Preferirà le classi definite (ad esempio attraverso uno script) su classi del genitore. Se hai uno script con la classe Data in esso, userà quella classe Data, anche se il genitore è normalmente il classloader definitivo