2014-11-30 4 views
9

У меня есть два класса (A и B), которые загружаются различными ClassLoaders. Кроме того, у меня есть третий класс, который предлагает статические методы getter и setter. Я надеюсь, что следующая картина может прояснить ситуацию:Доступ к статическому методу из классов, загружаемых различными ClassLoaders

enter image description here

Data класса выглядит следующим образом:

public class Data { 

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

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

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

В классе A, я хочу, чтобы установить статическое значение Data и B Я хочу для получения этого значения. Однако в B я всегда получаю исходное значение (которое равно "<fill in>"). У меня есть только базовое понимание ClassLoader, так что я не слишком уверен, что происходит под капотом. Я думал, что оба ClassLoaders (clA и clB) будут распространяться на их родителей ClassLoader и что я получу тот же класс Data в обоих. Может ли кто-нибудь дать мне некоторую обратную связь о поведении или указать мне в сторону взгляда?

Update

При печати на hashCode() обоих Data классов, я получаю различные значения для них (то есть, очевидно, я не получаю доступ к одному классу). Есть ли простой способ проиллюстрировать иерархию ClassLoader?

+3

Вы уверены, что и «класс A», и «класс B» разговаривают с «тот же самый класс' Data', что и в классе 'Data', загруженном одним загрузчиком классов? Если 'Data' загружается разными загрузчиками классов, а' class A' и 'class B' разговаривают с разными версиями, то ожидается то, что вы видите. Это очень зависит от иерархии загрузчика классов, поэтому немного этого контекста поможет. – mystarrocks

+0

@mystarrocks спасибо за отзывы, которые уже помогли. Кажется, я действительно не получаю одну и ту же ссылку на класс. Я соответствующим образом обновил свой вопрос. Спасибо! – WeSt

+0

Связаны ли эти классы с приложением, которое работает на сервере? Различные контейнеры используют различные методы загрузки классов. – mystarrocks

ответ

2

Если ваш вопрос заключается в том, как проиллюстрировать или визуализировать иерархию загрузчика классов для объектов, то вы можете пройти каждый класс classloader в коде. Вы упомянули, что вы используете заводным, так что пример будет выглядеть так:

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

} 

Я думаю, что вы найдете в вашем коде, два объекта данных фактически не загружается из тех же загрузчика классов, поэтому у них есть различные статические переменные.

я собрал образец, который имеет

  • Main (загружается из родительского загрузчика)
  • DataObj со статической String (нагруженной также из родительского загрузчика)
  • Loada, который создает экземпляр копии DataObj (загружается из ребенок загрузчика классов A)
  • LoadB, который инициализирует копию DataObj (загружается из ребенка B загрузчика классов)

Я вижу, что в то время как LoadA и LoadB имеют разные загрузчики классов, DataObj и статическая переменная исходят от обычного загрузчика классов.

Полный код по адресу: https://github.com/lucasmcgregor/groovy_classloader_test

Главный объект в заводной:

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

Результаты:

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 

Вы видите, что Loada и LoadB оба имеют разные загрузчики классов, но они совместно использовать родительский загрузчик классов.

Родительский загрузчик классов загружает DataObj для обоих экземпляров LoadA.dataObj и LoadB.dataObj.

LoadA.dataObj и LoadB.dataObj имеют разные хэш-коды.

Однако LoadA.dataObj.data и LoadB.dataObj.data имеют одинаковый хэш-код, потому что это статический объект. Они также имеют одинаковую ценность. LoadB создает экземпляр dataObj last и устанавливает строку «Loaded By B»

1

Я думаю, что Лукас ответил на ваш вопрос для иллюстрации иерархии. Я хочу добавить свой ответ, только чтобы прояснить некоторые напоминания о вопросе

В Java пара (определяющая загрузчик классов, класс) уникальна. Определение classloader здесь означает загрузчик, который фактически реализует класс как класс из байт-кода. Эта уникальность означает, что classloader, определяющий класс X, не может определить второй класс X, он должен иметь другое имя. Но другой классный загрузчик может определить класс. ClassLoaders структурированы в виде дерева (это не DAG, но это далеко продвинуто), и загрузчик классов должен сначала спросить своего родителя, если запрашивается для класса. Так может случиться, что данные существуют дважды, например, один раз в cIA и один раз в cIB. Чтобы этого избежать, вы обычно хотите, чтобы данные определялись загрузчиком классов, который является родителем для cIA и cIB. Это предполагает, что два загрузчика ведут себя в соответствии с ограничениями загрузчика классов, например, сначала спрашивают родителей.

Так как это также относится к скрипту Groovy, но нет никаких подробных сведений о настройке, мое предположение заключается в том, что ciB не имеет родителя, который знает данные, и что библиотека была указана в URL-адресе используемого GroovyClassLoader и что вы использовали GroovyShell. Вместо этого следует, что GroovyShell создается с использованием одного из аргументов classloader и что этот загрузчик классов является дочерним элементом загрузчика, определяющего Data, который также является родителем для cIA (родитель может быть одним и тем же загрузчиком во всех случаях, когда я использовал долгосрочный родитель).

Предупреждение ... GroovyClassLoader не является полностью полноценным загрузчиком классов. Он предпочтет определенные классы (например, через скрипт) над классами из родителя. Если у вас есть сценарий с классом Data в нем, он будет использовать этот класс Data, даже если родитель обычно является определяющим загрузчиком классов.

Смежные вопросы