Java, Classpath, Classloading => Aynı jar / projenin Birden Fazla Versiyonu


119

Bunun deneyimli kodlayıcılar için aptalca bir soru olabileceğini biliyorum. Ancak, projemde kullanılan diğer çerçevelerin / kavanozların bazılarının gerektirdiği bir kitaplığım (bir http istemcisi) var. Ancak hepsi aşağıdaki gibi farklı ana sürümler gerektirir:

httpclient-v1.jar => Required by cralwer.jar
httpclient-v2.jar => Required by restapi.jar
httpclient-v3.jar => required by foobar.jar

Sınıf yükleyici, onları bir şekilde ayıracak kadar akıllı mı? Büyük ihtimalle değil mi? Bir Sınıfın her üç kavanozda da aynı olması durumunda Classloader bunu nasıl halleder? Hangisi yüklü ve neden?

Classloader sadece bir kavanoz mu alıyor yoksa sınıfları rastgele karıştırıyor mu? Öyleyse, örneğin Version-1.jar'dan bir sınıf yüklenirse, aynı sınıf yükleyiciden yüklenen diğer tüm sınıflar aynı jar'e gidecek mi?

Bu sorunu nasıl çözüyorsunuz?

Kavanozları bir şekilde "required.jar" a "dahil etmek" için bir numara var mı, böylece bunlar, tarafından "bir birim / paket" olarak Classloaderveya bir şekilde bağlantılı olarak görülüyor mu?

Yanıtlar:


58

Sınıf yükleyiciyle ilgili sorunlar oldukça karmaşık bir konudur. Her durumda bazı gerçekleri aklınızda bulundurmalısınız:

  • Bir uygulamadaki sınıf yükleyiciler genellikle birden fazla sınıf yükleyicidir. Bootstrap sınıfı yükleyici, uygun. Yeni bir sınıf başlattığınızda, daha spesifik olan sınıf yükleyici çağrılır. Yüklemeye çalıştığınız sınıfa bir başvuru bulamazsa, siz önyükleme sınıf yükleyicisine ulaşıncaya kadar üst sınıfına ve bu şekilde devam eder. Hiçbiri yüklemeye çalıştığınız sınıfa bir başvuru bulamazsa, bir ClassNotFoundException alırsınız.

  • Aynı ikili ada sahip, aynı sınıf yükleyici tarafından aranabilen iki sınıfınız varsa ve bunlardan hangisini yüklediğinizi bilmek istiyorsanız, yalnızca belirli bir sınıf yükleyicinin bir sınıf adını çözümlemeye çalıştığı yöntemi inceleyebilirsiniz.

  • Java dili belirtimine göre, bir sınıf ikili adı için bir benzersizlik kısıtlaması yoktur, ancak görebildiğim kadarıyla her bir sınıf yükleyici için benzersiz olmalıdır.

Aynı ikili ada sahip iki sınıfı yüklemenin bir yolunu bulabilirim ve bu, varsayılan davranışı geçersiz kılan iki farklı sınıf yükleyici tarafından yüklenmelerini (ve tüm bağımlılıklarını) içerir. Kaba bir örnek:

    ClassLoader loaderA = new MyClassLoader(libPathOne);
    ClassLoader loaderB = new MyClassLoader(libPathTwo);
    Object1 obj1 = loaderA.loadClass("first.class.binary.name", true)
    Object2 obj2 = loaderB.loadClass("second.class.binary.name", true);

Sınıf yükleyici özelleştirmesini her zaman zor bir görev buldum. Mümkünse birden fazla uyumsuz bağımlılıktan kaçınmayı tercih ederim.


14
Bootstrap sınıfı yükleyici, uygun. Yeni bir sınıf başlattığınızda, daha spesifik olan sınıf yükleyici çağrılır. Yüklemeye çalıştığınız sınıfa bir referans bulamazsa, üst sınıfına delege eder Lütfen bana katlanın, ancak bu, varsayılan olarak Parent First olan sınıf yükleyici politikasına bağlıdır. Diğer bir deyişle, alt sınıf, önce üst sınıfından sınıfı yüklemesini isteyecek ve yalnızca tüm hiyerarşi onu yükleyemezse yüklenecektir, hayır ??
deckingraj

5
Hayır - tipik olarak bir sınıf yükleyici, sınıfın kendisini aramadan önce üst sınıfına yetki verir. Classloader için javadoc sınıfına bakın.
Joe Kearney

1
Sanırım tomcat bunu burada anlatıldığı şekilde yapıyor, ancak "geleneksel" delegasyon önce ebeveyne
sormalı

@deckingraj: biraz googling yaptıktan sonra bunu oracle belgelerinde buldum: "Yetki verme tasarımında, bir sınıf yükleyici, bir sınıfın kendisini yüklemeye çalışmadan önce sınıf yüklemesini üst sınıfına devreder . [...] Ana sınıf yükleyici bir sınıfı yükleyemezse, sınıf yükleyici, sınıfın kendisini yüklemeye çalışır. Gerçekte, bir sınıf yükleyici yalnızca üst öğe tarafından kullanılamayan sınıfları yüklemekten sorumludur ". Daha fazla araştıracağım. Bu varsayılan uygulama olarak ortaya çıkarsa yanıtı buna göre güncelleyeceğim. ( docs.oracle.com/cd/E19501-01/819-3659/beadf/index.html )
Luca Putzu

20

Her sınıf yükü tam olarak bir sınıf seçer. Genellikle birincisi bulunur.

OSGi , aynı kavanozun birden fazla sürümünün sorununu çözmeyi amaçlamaktadır. Equinox ve Apache Felix , OSGi için en yaygın açık kaynak uygulamalarıdır.


6

Sınıf yükleyici, önce sınıf yolunda olan jar'den sınıfları yükleyecektir. Normalde, uyumsuz kitaplık sürümleri paketlerde farklılık gösterir, ancak olası olmayan bir durumda bunlar gerçekten uyumsuzdur ve bir tane ile değiştirilemez - jarjar'ı deneyin.


6

Sınıf yükleyiciler, sınıfı isteğe bağlı olarak yükler. Bu, uygulamanızın ve ilgili kitaplıkların ilk ihtiyaç duyduğu sınıfın diğer sınıflardan önce yükleneceği anlamına gelir; bağımlı sınıfları yükleme isteği genellikle bağlı bir sınıfın yükleme ve bağlama işlemi sırasında verilir.

LinkageErrorSınıf yükleyiciler için yinelenen sınıf tanımlarıyla karşılaşıldığını belirten durumlarla karşılaşmanız olasıdır (yükleyicinin sınıf yolunda aynı ada sahip iki veya daha fazla sınıf varsa) önce hangi sınıfın yüklenmesi gerektiğini belirlemeye çalışmazsınız. Bazen, sınıf yükleyici, sınıf yolunda meydana gelen birinci sınıfı yükler ve yinelenen sınıfları yok sayar, ancak bu, yükleyicinin uygulanmasına bağlıdır.

Bu tür hataları çözmek için önerilen uygulama, çakışan bağımlılıkları olan her kitaplık kümesi için ayrı bir sınıf yükleyici kullanmaktır. Bu şekilde, bir sınıf yükleyici bir kitaplıktan sınıfları yüklemeye çalışırsa, bağımlı sınıflar, diğer kitaplıklara ve bağımlılıklara erişimi olmayan aynı sınıf yükleyici tarafından yüklenir.


1

URLClassLoaderSınıfları bir diff-2 sürümünden yüklemek için for gereksinimini kullanabilirsiniz :

URLClassLoader loader1 = new URLClassLoader(new URL[] {new File("httpclient-v1.jar").toURL()}, Thread.currentThread().getContextClassLoader());
URLClassLoader loader2 = new URLClassLoader(new URL[] {new File("httpclient-v2.jar").toURL()}, Thread.currentThread().getContextClassLoader());

Class<?> c1 = loader1.loadClass("com.abc.Hello");

Class<?> c2 = loader2.loadClass("com.abc.Hello");

BaseInterface i1 = (BaseInterface) c1.newInstance();

BaseInterface i2 = (BaseInterface) c2.newInstance();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.