如何查找正在使用的JAXP实现和它加载自哪里?

39

我想提供一些关于正在使用的JAXP实现以及加载它的JAR文件的诊断信息。

实现这个目标的一种方式是创建一个例如DocumentBuilderFactory的实例,然后检查该类的属性:

private static String GetJaxpImplementation() {
    DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
    Class<? extends DocumentBuilderFactory> c = documentBuilderFactory.getClass();
    Package p = c.getPackage();
    CodeSource source = c.getProtectionDomain().getCodeSource();
    return MessageFormat.format(
            "Using JAXP implementation ''{0}'' ({1}) version {2} ({3}){4}",
            p.getName(),
            p.getImplementationVendor(),
            p.getSpecificationVersion(),
            p.getImplementationVersion(),
            source == null ? "." : " loaded from: " + source.getLocation());
}

有没有更好的方法来实现这个,也许不需要创建一个DocumentBuilderFactory

5个回答

64

根据选择实现的过程,预测具体的JAXP工厂实现会被加载是相当困难的,必须创建一个实例才能知道。以下是来自官方JAXP FAQ(第14个问题)的说明:

当应用程序想要创建一个新的JAXP DocumentBuilderFactory实例时,它会调用静态方法DocumentBuilderFactory.newInstance()。这会按照以下顺序搜索具体的DocumentBuilderFactory子类的名称:

  1. 如果存在且可访问,则使用系统属性(如javax.xml.parsers.DocumentBuilderFactory)的值。
  2. 如果存在,则使用文件$JAVA_HOME/jre/lib/jaxp.properties的内容。
  3. 使用Jar文件规范中指定的Jar服务提供程序发现机制。一个Jar文件可以具有名为META-INF/services/javax.xml.parsers.DocumentBuilderFactory的资源(即嵌入式文件),其中包含要实例化的具体类的名称。
  4. 备用平台默认实现。

除此之外,每个单独的JAXP工厂都可以具有独立的实现规范。通常会使用一个解析器实现和另一个XSLT实现,但选择机制的粒度允许您进行更多的混合和匹配。

以下代码将输出有关四个主要JAXP工厂的信息:

private static void OutputJaxpImplementationInfo() {
    System.out.println(getJaxpImplementationInfo("DocumentBuilderFactory", DocumentBuilderFactory.newInstance().getClass()));
    System.out.println(getJaxpImplementationInfo("XPathFactory", XPathFactory.newInstance().getClass()));
    System.out.println(getJaxpImplementationInfo("TransformerFactory", TransformerFactory.newInstance().getClass()));
    System.out.println(getJaxpImplementationInfo("SAXParserFactory", SAXParserFactory.newInstance().getClass()));
}

private static String getJaxpImplementationInfo(String componentName, Class componentClass) {
    CodeSource source = componentClass.getProtectionDomain().getCodeSource();
    return MessageFormat.format(
            "{0} implementation: {1} loaded from: {2}",
            componentName,
            componentClass.getName(),
            source == null ? "Java Runtime" : source.getLocation());
}

以下示例输出演示了三种不同的JAXP实现(内置Xerces,外部Xerces 2.8 JAR和Xalan)混合使用的情况:

DocumentBuilderFactory implementation: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl loaded from: file:/C:/Projects/Scratch/lib/xerces-2.8.0.jar
XPathFactory implementation: com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl loaded from: Java Runtime
TransformerFactory implementation: org.apache.xalan.processor.TransformerFactoryImpl loaded from: file:/C:/Projects/Scratch/lib/xalan.jar
SAXParserFactory implementation: org.apache.xerces.jaxp.SAXParserFactoryImpl loaded from: file:/C:/Projects/Scratch/lib/xerces-2.8.0.jar

14

4
这很简单,你只需要设置即可。
System.setProperty("jaxp.debug", "1");

这个追踪器将告诉你使用哪种实现,并且 JAXP 使用的方式。


2

在“后备平台默认实现”之前,还有另一个位置被搜索,那就是java.endorsed.dirs目录,如Java认可标准覆盖机制中所述。

认可标准覆盖机制提供了一种方式,可以将实现认可标准或独立技术的后续版本的类和接口纳入Java平台。


顺便提一下,自8u40版本以来已经被弃用,您可以使用“-XX:+ CheckEndorsedAndExtDirs”检查它是否仍在使用。 - eckes

1

这要看情况,但通常不行。

DocumentBuilderFactory.newInstance()将返回配置在系统属性“javax.xml.parsers.DocumentBuilderFactory”中的DocumentBuilderFactory实现,或者如果未设置系统属性,则返回JRE的默认工厂。默认工厂很可能是在newInstance方法的实现中硬编码的,因此无法访问。

如果设置了系统属性,您至少可以在相关类加载器上使用getResource方法获取URL,从中类加载器将加载相应的类文件。如果它来自jar文件,则应该能够从jar: URL中提取文件名(或源URL)。如果手动从类路径读取元数据文件,则还应该提供详细的包信息。

如果未设置系统属性,则我非常确定您没有办法获取所需的信息,除非像您已经做的那样创建一个新工厂。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接