如何使用 classloader 加载 apach xmlbeans class?
How do I load apach xmlbeans class with classloader?
我们有一个默认生成 pdf 输出的报告应用程序,但您可以编写自己的 classes 来生成任何其他输出格式。这样我就使用 apache poi 10.0 生成了 xls 文件。但是,现在出现了生成 xlsx 文件的请求。当我尝试使用此代码创建工作簿时:
XSSFWorkbook wbTemplate=new XSSFWorkbook()
我收到错误:
java.lang.NoSuchMethodError: org.apache.xmlbeans.XmlOptions.setSaveAggressiveNamespaces()Lorg/apache/xmlbeans/XmlOptions;
我发现该应用程序已经使用了非常旧版本的 xmlbeans 文件,当然不包含上述方法。首先,我尝试用更新的版本替换 xml bean 文件以防万一,但应用程序冻结了。
我的下一个想法是使用 classLoader,当应用程序运行我的 class 生成 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);
但是当它想要获取 "setSaveAggressiveNamespaces" 方法名称时,我又得到了这个函数不存在的错误。
然后我将此 class 的所有函数名称写入文件,确实,该名称不存在。但是存在另一种叫 "setSaveAggresiveNamespaces" 和一个 S 的!如果我调用它,它会起作用,但是当然,当我不想创建 XSSF 工作簿时,我仍然会收到一条消息,指出 setSaveAggressiveNamespaces(带有双 SS)不存在。
但是 setSaveAggressiveNamespaces 应该在 class 中,因为这是随 apache poi 包一起提供的。
在这种情况下我该怎么做才能让它发挥作用?
该应用程序在 java 1.6
下运行
提前感谢您的回答。
更新
Axel,这就是我现在加载 class:
的方式
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);
}
然后我在生成报告的 class 的第一行调用上述函数。前面什么都没有。
RESOURCE 在日志中写为:
"RESOURCES:jar:file:/C:/HOME/Installs/Apache POI/poi-3.10/ooxml-lib/xmlbeans-2.3.0.jar!/org/apache/xmlbeans/XmlOptions.class"
URLClassLoader(java.net.URL[]) 状态:
Constructs a new URLClassLoader for the specified URLs using the
default delegation parent ClassLoader.
因此也将使用默认委托父类加载器,因此如果找到 org.apache.xmlbeans.XmlOptions
将从那里加载,而不是从额外给定的 URL
.
所以我们需要而不是使用默认委托父类加载器。 URLClassLoader(java.net.URL[], null) 正在这样做。
示例:
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
}
}
我的调用如下:
axel@arichter:~/Dokumente/JAVA/poi/poi-4.0.0$ java -cp .:./*:./lib/*:./ooxml-lib/* UseURLClassLoader
它产生:
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
所以一开始 class 是使用默认父 class 加载器加载的。对我来说,它加载 org.apache.xmlbeans.XmlOptions
比较新的 xmlbeans-3.0.1.jar
更远。对你来说,它加载得比旧的 xmlbeans-1.*.jar
更远。那是因为这些 jar 位于默认父 class 加载程序的 class 路径中。
第二个代码部分然后将默认父 class 加载器设置为空,因此 class 仅使用此 class 加载器加载。
但是乱用 class 加载程序是一团糟。正如我的代码所暗示的那样,将默认父 class 加载器设置为 null,我们需要为当前 class 加载器提供所有需要的 class 源。这通常变得非常昂贵。所以 not 在 class 路径中有旧的 jar
s 总是比乱用 class 加载器更好的解决方案。
我们有一个默认生成 pdf 输出的报告应用程序,但您可以编写自己的 classes 来生成任何其他输出格式。这样我就使用 apache poi 10.0 生成了 xls 文件。但是,现在出现了生成 xlsx 文件的请求。当我尝试使用此代码创建工作簿时:
XSSFWorkbook wbTemplate=new XSSFWorkbook()
我收到错误:
java.lang.NoSuchMethodError: org.apache.xmlbeans.XmlOptions.setSaveAggressiveNamespaces()Lorg/apache/xmlbeans/XmlOptions;
我发现该应用程序已经使用了非常旧版本的 xmlbeans 文件,当然不包含上述方法。首先,我尝试用更新的版本替换 xml bean 文件以防万一,但应用程序冻结了。
我的下一个想法是使用 classLoader,当应用程序运行我的 class 生成 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);
但是当它想要获取 "setSaveAggressiveNamespaces" 方法名称时,我又得到了这个函数不存在的错误。 然后我将此 class 的所有函数名称写入文件,确实,该名称不存在。但是存在另一种叫 "setSaveAggresiveNamespaces" 和一个 S 的!如果我调用它,它会起作用,但是当然,当我不想创建 XSSF 工作簿时,我仍然会收到一条消息,指出 setSaveAggressiveNamespaces(带有双 SS)不存在。 但是 setSaveAggressiveNamespaces 应该在 class 中,因为这是随 apache poi 包一起提供的。
在这种情况下我该怎么做才能让它发挥作用? 该应用程序在 java 1.6
下运行提前感谢您的回答。
更新
Axel,这就是我现在加载 class:
的方式 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);
}
然后我在生成报告的 class 的第一行调用上述函数。前面什么都没有。
RESOURCE 在日志中写为: "RESOURCES:jar:file:/C:/HOME/Installs/Apache POI/poi-3.10/ooxml-lib/xmlbeans-2.3.0.jar!/org/apache/xmlbeans/XmlOptions.class"
URLClassLoader(java.net.URL[]) 状态:
Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader.
因此也将使用默认委托父类加载器,因此如果找到 org.apache.xmlbeans.XmlOptions
将从那里加载,而不是从额外给定的 URL
.
所以我们需要而不是使用默认委托父类加载器。 URLClassLoader(java.net.URL[], null) 正在这样做。
示例:
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
}
}
我的调用如下:
axel@arichter:~/Dokumente/JAVA/poi/poi-4.0.0$ java -cp .:./*:./lib/*:./ooxml-lib/* UseURLClassLoader
它产生:
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
所以一开始 class 是使用默认父 class 加载器加载的。对我来说,它加载 org.apache.xmlbeans.XmlOptions
比较新的 xmlbeans-3.0.1.jar
更远。对你来说,它加载得比旧的 xmlbeans-1.*.jar
更远。那是因为这些 jar 位于默认父 class 加载程序的 class 路径中。
第二个代码部分然后将默认父 class 加载器设置为空,因此 class 仅使用此 class 加载器加载。
但是乱用 class 加载程序是一团糟。正如我的代码所暗示的那样,将默认父 class 加载器设置为 null,我们需要为当前 class 加载器提供所有需要的 class 源。这通常变得非常昂贵。所以 not 在 class 路径中有旧的 jar
s 总是比乱用 class 加载器更好的解决方案。