Java 如何使用classloader加载ApachXMLBeans类?

Java 如何使用classloader加载ApachXMLBeans类?,java,apache-poi,classloader,Java,Apache Poi,Classloader,我们有一个默认生成pdf输出的报告应用程序,但是您可以编写自己的类来生成任何其他输出格式。通过这种方式,我使用ApachePOI10.0生成了xls文件。但是,现在出现了生成xlsx文件的请求。当我尝试使用以下代码创建工作簿时: XSSFWorkbook wbTemplate=new XSSFWorkbook() 我得到了一个错误: java.lang.NoSuchMethodError: org.apache.xmlbeans.XmlOptions.setSaveAggressiveName

我们有一个默认生成pdf输出的报告应用程序,但是您可以编写自己的类来生成任何其他输出格式。通过这种方式,我使用ApachePOI10.0生成了xls文件。但是,现在出现了生成xlsx文件的请求。当我尝试使用以下代码创建工作簿时:

XSSFWorkbook wbTemplate=new XSSFWorkbook()
我得到了一个错误:

java.lang.NoSuchMethodError: org.apache.xmlbeans.XmlOptions.setSaveAggressiveNamespaces()Lorg/apache/xmlbeans/XmlOptions;
我发现应用程序已经使用了一个非常旧版本的xmlbeans文件,当然它不包含上述方法。首先,我尝试用一个更新的版本替换XMLBean文件,以防运气好,但应用程序冻结了

我的下一个想法是使用classLoader,当应用程序运行我的类来生成xlsx文件时,我加载上面的方法。为此,我实施了互联网上的解决方案:

URL[] classLoaderUrls = new URL[]{new URL("file:/C:/HOME/Installs/Apache POI/poi-3.10/ooxml-lib/xmlbeans-2.6.0.jar")};
URLClassLoader urlClassLoader = new URLClassLoader(classLoaderUrls);
Class<?> beanClass = urlClassLoader.loadClass("org.apache.xmlbeans.XmlOptions");
Constructor<?> constructor = beanClass.getConstructor();
Object beanObj = constructor.newInstance();
Method[] m=beanClass.getMethods();
Method method = beanClass.getMethod("setSaveAggressiveNamespaces");
method.invoke(beanObj);
URL[]classLoaderUrls=newurl[]{newurl(“文件:/C:/HOME/Installs/Apache-POI/POI-3.10/ooxml-lib/xmlbeans-2.6.0.jar”);
URLClassLoader URLClassLoader=新的URLClassLoader(classLoaderUrls);
类beanClass=urlClassLoader.loadClass(“org.apache.xmlbeans.XmlOptions”);
Constructor=beanClass.getConstructor();
对象beanObj=constructor.newInstance();
方法[]m=beanClass.getMethods();
方法Method=beanClass.getMethod(“setSaveAggressiveNamespaces”);
方法调用(beanObj);
但是,当它想要得到“setSaveAggressiveNamespaces”方法名时,我又得到了一个错误,即这个函数不存在。 然后我把这个类的所有函数名都写进了一个文件,这个名称确实不存在。但是存在另一个名为“SetSaveAggressiveNamespaces”的名称空间,它带有一个S!如果我调用它,它会工作,但当然,当我不想创建XSSF工作簿时,我仍然会收到一条消息,即setSaveAggressiveNamespaces(带双SS)不存在。 但是setSaveAggressiveNamespaces应该在类中,因为这是ApachePOI包附带的

在这种情况下,我能做些什么来让它工作? 该应用程序在Java1.6下运行

提前感谢您的回答

更新

Axel,这就是我现在加载类的方式:

 public void customClassLoader() throws Exception
{
    URL[] classLoaderUrls = new URL[]{new URL("file:/C:/HOME/Installs/Apache POI/poi-3.10/ooxml-lib/xmlbeans-2.3.0.jar")};
    URLClassLoader urlClassLoader = new URLClassLoader(classLoaderUrls,null);
    Class<?> beanClass = urlClassLoader.loadClass("org.apache.xmlbeans.XmlOptions");
    log("RESOURCES:" +beanClass.getResource("/org/apache/xmlbeans/XmlOptions.class"));
    Constructor<?> constructor = beanClass.getConstructor();
    Object beanObj = constructor.newInstance();
    Method[] m=beanClass.getMethods();
    for (int i=0;i<m.length;++i)
        log("QQQ:" +String.valueOf(i)+".: "+ m[i].getName());
    Method method = beanClass.getMethod("setSaveAggressiveNamespaces");
    method.invoke(beanObj);
}
public void customClassLoader()引发异常
{
URL[]classLoaderUrls=newURL[]{newURL(“文件:/C:/HOME/Installs/Apache-POI/POI-3.10/ooxml-lib/xmlbeans-2.3.0.jar”);
URLClassLoader URLClassLoader=新的URLClassLoader(classLoaderUrls,null);
类beanClass=urlClassLoader.loadClass(“org.apache.xmlbeans.XmlOptions”);
日志(“RESOURCES:+beanClass.getResource(“/org/apache/xmlbeans/XmlOptions.class”);
Constructor=beanClass.getConstructor();
对象beanObj=constructor.newInstance();
方法[]m=beanClass.getMethods();
对于(int i=0;i状态:

使用 默认委托父类加载器

因此,也将使用默认的委托父类加载器,因此如果找到
org.apache.xmlbeans.XmlOptions
,则将从那里加载
URL

因此,我们需要而不是使用默认的委托父类加载器

例如:

import java.net.URL;
import java.net.URLClassLoader;
import java.lang.reflect.Constructor;

public class UseURLClassLoader {

 public static void main(String[] args) throws Exception {
  URL[] classLoaderUrls;
  URLClassLoader urlClassLoader;
  Class<?> beanClass;

  classLoaderUrls = new URL[]{new URL("file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/ooxml-lib/xmlbeans-2.6.0.jar")};
  urlClassLoader = new URLClassLoader(classLoaderUrls); //default delegation parent ClassLoader is used
  beanClass = urlClassLoader.loadClass("org.apache.xmlbeans.XmlOptions");
System.out.println(beanClass.getResource("/org/apache/xmlbeans/XmlOptions.class")); //class is loaded using default parent class loader

  URL context = new URL("file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/");
  classLoaderUrls = new URL[] {
   new URL(context, "poi-3.10.1-20140818.jar"),
   new URL(context, "poi-ooxml-3.10.1-20140818.jar"),
   new URL(context, "poi-ooxml-schemas-3.10.1-20140818.jar"),
   // maybe others also necessary
   new URL(context, "lib/commons-codec-1.5.jar"),
   // maybe others also necessary
   new URL(context, "ooxml-lib/xmlbeans-2.6.0.jar")
   // maybe others also necessary
  };
  for (int i = 0; i < classLoaderUrls.length; i++) {
System.out.println(classLoaderUrls[i]);
  }
  urlClassLoader = new URLClassLoader(classLoaderUrls, null); //set default parent class loader null
  beanClass = urlClassLoader.loadClass("org.apache.xmlbeans.XmlOptions");
System.out.println(beanClass.getResource("/org/apache/xmlbeans/XmlOptions.class")); //class is loaded using this class loader

 }

}
它产生:

jar:file:/home/axel/Dokumente/JAVA/poi/poi-4.0.0/ooxml-lib/xmlbeans-3.0.1.jar!/org/apache/xmlbeans/XmlOptions.class
file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/poi-3.10.1-20140818.jar
file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/poi-ooxml-3.10.1-20140818.jar
file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/poi-ooxml-schemas-3.10.1-20140818.jar
file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/lib/commons-codec-1.5.jar
file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/ooxml-lib/xmlbeans-2.6.0.jar
jar:file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/ooxml-lib/xmlbeans-2.6.0.jar!/org/apache/xmlbeans/XmlOptions.class
因此,首先使用默认的父类加载器加载类。对我来说,它加载的
org.apache.xmlbeans.XmlOptions
距离较新的
xmlbeans-3.0.1.jar
。对你来说,它加载的距离较旧的
xmlbeans-1.*.jar
。这是因为这些jar位于默认父类加载器的类路径中

然后,第二个代码部分将默认的父类加载器设置为null,因此只使用该类加载器加载类


但是混淆类加载器是一个混乱。正如我的代码中所暗示的,将默认父类加载器设置为null,我们需要为当前类加载器提供所有所需的类源。这通常会变得非常昂贵。因此,在类路径中使用旧的
jar
s总是比m更好的解决方案essing与类加载器一起工作。

亲爱的Axel,谢谢你花时间用很好的示例编写如此详细的答案。我会尽快测试它并返回给你。Alex,这很奇怪。基本上你的解决方案是有效的,但我必须启动报告生成器两次。如果我第一次启动它,我会收到与setSaveA相同的消息ggressiveNamespaces不存在。但是第二次我收到一条消息,NoClassDefFind for XSSFWorkbook。我想这在这个阶段是可以的。你知道为什么我必须运行两次报告才能使classsloader生效吗?那么现在更新的代码有什么问题吗?你告诉我的日志中写的内容似乎是正确的没有?如果没有,为什么没有?
jar:file:/home/axel/Dokumente/JAVA/poi/poi-4.0.0/ooxml-lib/xmlbeans-3.0.1.jar!/org/apache/xmlbeans/XmlOptions.class
file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/poi-3.10.1-20140818.jar
file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/poi-ooxml-3.10.1-20140818.jar
file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/poi-ooxml-schemas-3.10.1-20140818.jar
file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/lib/commons-codec-1.5.jar
file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/ooxml-lib/xmlbeans-2.6.0.jar
jar:file:/home/axel/Dokumente/JAVA/poi/poi-3.10.1/ooxml-lib/xmlbeans-2.6.0.jar!/org/apache/xmlbeans/XmlOptions.class