簡體   English   中英

使用packageName和Classloader實例化JAXBContext時,會混淆ClassNotFoundException

[英]Confusing ClassNotFoundException when instantiating JAXBContext with packageName and Classloader

我正在嘗試使用Java中的JAXB將XML文件解組為生成的類結構。 我遇到了一個令人困惑的問題,我在JAXBContext.newInstance(packageName, classLoader)提到的類加載器顯然找不到一些必要的類來實例化模式類,但是當我手動搜索提供的類加載器以獲取所需的類時,他們在那里:

URLClassLoader cl = this.getJaxbClassloader();
try 
{
    cl.loadClass("org.postgresql.util.PGInterval");
    Log.error("Found class [" + name + "] in provided classloader");
} 
catch (ClassNotFoundException e) 
{
    Log.error("Unable to find class [" + name + "]  in provided classloader");
}

JAXBContext ctx = JAXBContext.newInstance( "com.comp.gen", cl);

getJaxbClassloader()方法只是創建一個新的URLClassLoader,加載生成的類所需的一些特定jar,然后將系統類加載器設置為父類。 生成的類使用了一些我放入類加載器的postgresql庫,這是我遇到問題的資源。 JAXB正確地在提供的包中找到ObjectFactory類,它只是生成的類本身的實例化,這似乎是問題所在。

運行此代碼的結果是手動調用cl.loadClass("org.postgresql.util.PGInterval"); 工作正常,它記錄第一個聲明,說它找到了類,沒有拋出異常。 但是當實例化JAXBContext時,它會在完全相同的資源上拋出CNFE:

java.lang.ClassNotFoundException: org.postgresql.util.PGInterval
   at java.net.URLClassLoader.findClass(URLClassLoader.java:600)
   at java.lang.ClassLoader.loadClassHelper(ClassLoader.java:772)
   at java.lang.ClassLoader.loadClass(ClassLoader.java:745)
   at java.lang.ClassLoader.loadClass(ClassLoader.java:726)
   ... 78 more

更徹底的堆棧跟蹤:

java.lang.NoClassDefFoundError: org.postgresql.util.PGInterval
    at java.lang.Class.getDeclaredFieldsImpl(Native Method)
    at java.lang.Class.getDeclaredFields(Class.java:740)
    at com.sun.xml.bind.v2.model.nav.ReflectionNavigator.getDeclaredFields(ReflectionNavigator.java:249)
    at com.sun.xml.bind.v2.model.nav.ReflectionNavigator.getDeclaredFields(ReflectionNavigator.java:58)
    at com.sun.xml.bind.v2.model.impl.ClassInfoImpl.findFieldProperties(ClassInfoImpl.java:370)
    at com.sun.xml.bind.v2.model.impl.RuntimeClassInfoImpl.getProperties(RuntimeClassInfoImpl.java:176)
    at com.sun.xml.bind.v2.model.impl.ModelBuilder.getClassInfo(ModelBuilder.java:243)
    at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.getClassInfo(RuntimeModelBuilder.java:100)
    at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.getClassInfo(RuntimeModelBuilder.java:81)
    at com.sun.xml.bind.v2.model.impl.ModelBuilder.getClassInfo(ModelBuilder.java:209)
    at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.getClassInfo(RuntimeModelBuilder.java:95)
    at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.getClassInfo(RuntimeModelBuilder.java:81)
    at com.sun.xml.bind.v2.model.impl.ModelBuilder.getTypeInfo(ModelBuilder.java:315)
    at com.sun.xml.bind.v2.model.impl.RegistryInfoImpl.<init>(RegistryInfoImpl.java:99)
    at com.sun.xml.bind.v2.model.impl.ModelBuilder.addRegistry(ModelBuilder.java:357)
    at com.sun.xml.bind.v2.model.impl.ModelBuilder.getTypeInfo(ModelBuilder.java:327)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:466)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:302)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1136)
    at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:154)
    at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:121)
    at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:202)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:95)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:56)
    at java.lang.reflect.Method.invoke(Method.java:620)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:184)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:144)
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:346)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:443)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:406)

任何人都知道這里出了什么問題? 我的印象是(並且JAXBContext文檔支持這個),它將使用提供的類加載器來查找實例化類所需的實現類,因此,鑒於資源似乎在我提供的類加載器中,為什么JAXB無法找到它?

編輯:添加使用PGInterval資源的生成類的相關部分:

import org.postgresql.util.PGInterval;
...
... 
...
@XmlElement(name = "time_to_live", required=false)
protected PGInterval time_to_live;

public PGInterval gettime_to_live()
{
    return time_to_live;
}

public void settime_to_live(PGInterval time_to_live)
{
    this.time_to_live = time_to_live;
}

我想值得注意的是,這是生成的類中唯一的導入不是在java的標准庫中。

根據此FAQ ,在應用程序容器和服務器中使用JAXB時,可能會發生類加載問題。 建議的解決方案是在創建JAXBContext時使用當前的類加載器:

JAXBContext.newInstance( "com.comp.gen", this.getClass().getClassLoader() );

編輯:通過查看堆棧跟蹤中的相關代碼,指定的類加載器用於:

  1. 加載位於指定包com.comp.genObjectFactory類。
  2. 加載位於包中的jaxb.index文件中指定的類。

比照 com.sun.xml.internal.bind.v2.ContextFactory#createContext(String contextPath, ClassLoader classLoader, Map<String,Object> properties)

// look for ObjectFactory and load it
final Class<?> o;
try {
    o = classLoader.loadClass(pkg+".ObjectFactory");
    classes.add(o);
    ...

// look for jaxb.index and load the list of classes
try {
    indexedClasses = loadIndexedClasses(pkg, classLoader);
} catch (IOException e) {
    ...

從那時起,似乎JAXB使用某種反射來從這些已經加載的類加載所有靜態可達的類。 這也是JAXBContext#newInstance(String contextPath, ClassLoader classLoader)Javadocs中提到的JAXBContext#newInstance(String contextPath, ClassLoader classLoader)

contextPath上列出的每個包必須滿足以下一個或兩個條件,否則將拋出JAXBException:

  1. 它必須包含ObjectFactory.class
  2. 它必須包含jaxb.in​​dex

jaxb.in​​dex的格式

該文件包含以換行符分隔的類名列表。 空格和制表符以及空行將被忽略。 注釋字符是'#'(0x23); 在每一行上,忽略第一個注釋字符后面的所有字符。 該文件必須以UTF-8編碼。 從列出的類中可以訪問的類(如newInstance(Class...)定義newInstance(Class...)也會在JAXBContext中注冊

我假設(但這是我不確定的部分......)所有可到達的類也將使用您提供的類加載器加載。 但顯然在引用路徑的某處, org.postgresql.util.PGInterval 沒有org.postgresql.util.PGInterval加載器加載。 如果引用org.postgresql.util.PGInterval的類本身未由您的自定義類加載器加載,而是由父(系統)類加載器加載,則可能就是這種情況。 這意味着您可能希望確保自定義類加載器能夠將所有類從頂級類加載到org.postgresql.util.PGInterval類。

所以我最終想出來了。 在我工作的代碼庫的深處,一位先前的開發人員創建了一個使用ClassLoader.getSystemClassLoader()作為其父級的自定義動態類加載器。 這個自定義類加載器實際上用於從磁盤加載生成的JAXB類,我正在使用該實例的類加載器來處理JAXB。 一旦我看到我立即知道問題。

我運行的環境是tomcat上的restapi,所以我的組件級別的系統類加載器只包含引導tomcat所需的catalina jar。

以前的開發人員只在獨立的JVM中運行他的代碼,在那里他提供了一個巨大的類路徑。 因此,雖然這在技術上是一個環境問題,但根本原因是使用ClassLoader.getSystemClassLoader()作為新ClassLoader的父級。 將父級更改為更符合邏輯的內容,即包含類的類加載器解決了問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM