当前位置: 首页 > 知识库问答 >
问题:

使用packageName和Classloader实例化JAXBContext时混淆ClassNotFoundException

谢英光
2023-03-14

我试图在java中使用JAXB将XML文件解压到生成的类结构中。我遇到了一个令人困惑的问题,我将类加载器交给JAXBContext。newInstance(packageName,classLoader)显然无法找到实例化架构类所需的一些类,但当我手动搜索提供的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,加载生成的类所需的一些特定的罐,然后将系统类加载器设置为父类。生成的类使用一些我放在类加载器中的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的标准库中。

共有2个答案

蓝恩
2023-03-14

所以我最终找到了答案。在我工作的代码库的深处,一位先前的开发人员创建了一个使用类加载器的定制动态类加载器。getSystemClassLoader()作为其父级。这个自定义类加载器实际上用于从磁盘加载生成的JAXB类,我使用该实例的类加载器来处理JAXB。一旦我看到了,我马上就知道了问题所在。

我运行的环境是tomcat之上的restapi,因此我组件级别的系统类加载器只包含引导tomcat所需的catalina jar。

先前的开发人员只在独立的JVM中运行过他的代码,在那里他提供了一个巨大的类路径。因此,尽管这在技术上是一个环境问题,但根本原因是使用了类加载器。getSystemClassLoader()作为新类加载器的父级。将父对象更改为更符合逻辑的对象(即包含类的classloader)解决了此问题。

严子默
2023-03-14

根据这个常见问题,在应用程序容器和服务器中使用JAXB时可能会发生类加载问题。建议的解决方案是在创建JAXBContext时使用当前类加载器:

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

编辑:通过查看堆栈跟踪中的相关代码,指定的类加载器用于:

  1. 加载位于指定包com中的ObjectFactory类。公司。gen

参见com。太阳xml。内部的绑定v2。ContextFactory#createContext(字符串contextPath、类加载器、类加载器、映射

// 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 class Loader)的Javadocs中提到的:

ContextPath上列出的每个包必须满足以下条件中的一个或两个,否则将抛出JAXB异常:

  1. 它必须包含ObjectFactory。班级

jaxb的格式。指数

文件包含以换行符分隔的类名列表。空格和制表符以及空行将被忽略。注释字符为“#”(0x23);在每一行上,第一个注释字符后面的所有字符都将被忽略。文件必须用UTF-8编码。可访问的类,如newInstance(类…)中定义的 ,也在JAXBContext中注册。

我假设(但这是我不确定的部分...)所有可达的类也将使用您提供的类加载器加载。但是显然在引用路径的某个地方,PGIntervalorg.postgresql.util.没有被那个类加载器加载。如果引用org.postgresql.util.PGInterval的类本身不是由您的自定义类加载器加载的,而是由父(系统)类加载器加载的,则可能会出现这种情况。这意味着您可能需要确保您的自定义类加载器能够加载从顶级类到PGInterval类org.postgresql.util.所有类。

 类似资料:
  • 问题内容: 我正在尝试使用AngularUI路由器进行一些身份验证。看起来正是我所需要的。但是,仅当我拦截时可用。但是当我这样做时,它是空的,而我希望它是当前状态。 到目前为止,这是我的代码: 关于我在做什么错的任何指示? 问题答案: 我建议走更多的路。我们应该使用适当提供的事件。这是一个有效的例子 让我们观察一下简单的 (但不是幼稚的) 解决方案,它可以在以后扩展到任何程度。 首先,让我们定义用

  • 我在理解密钥库和信任库的概念方面有问题。 哪个应用程序保留keystore,哪个应用程序保留truststore? 我在网上找不到任何好的资源。如果有人能清楚地描述truststore和KeyStore之间的区别,我会非常感激吗?

  • 我想在Windows中使用WebMvcConfigurerAdapter添加资源处理程序,但在Linux中不起作用,所以我添加。 经过调试和测试,我发现两个bean都将在两个OS中创建,但是的重写函数只能在Windows中执行,的重写函数只能在Linux中执行。 这是另一个: @enalbemvc已添加到主类中

  • 本文向大家介绍Android 混淆代码详解及实例,包括了Android 混淆代码详解及实例的使用技巧和注意事项,需要的朋友参考一下 为了防止自己的劳动成果被别人窃取,混淆代码能有效防止被反编译,下面来总结以下混淆代码的步骤: 1. 大家也许都注意到新建一个工程会看到项目下边有这样proguard-project.txt一个文件,这个对混淆代码很重要,如果你不小心删掉了,没关系,从其他地方拷贝一个过

  • 本文向大家介绍PowerShell中实现混淆密码示例,包括了PowerShell中实现混淆密码示例的使用技巧和注意事项,需要的朋友参考一下 适用于PowerShell 3.0或者更高版本, 尽管我们并不推荐将密码硬编码在脚本文件中,但是仍旧有一些场景非如此不可。与将密码以一种纯文本的方式硬编码相比,我们还可以稍微花点心思,把密码混淆一下。虽然混淆密码是一种非常低级的保护方式,但是对于不懂Power

  • 我一直在摆弄Proguard配置,我想测试只是为了优化 但我仍然会遇到这样的错误: java.lang.IllegalArgumentExc0019:找不到[org/apache/log/log4j/core/jackson/Log4jXmlMoules](有1个已知的超级类)和[org/apache/log/log4j/core/jackson/Log4jJsonMoules](有4个已知的超级