如何在java中覆盖服务提供者
Posted
技术标签:
【中文标题】如何在java中覆盖服务提供者【英文标题】:how to override a service provider in java 【发布时间】:2013-07-22 08:12:15 【问题描述】:这是一个更一般的问题示例: 我正在使用 xstream 和woodstox,woodstox 在woodstox jar 注册com.ctc.wstx.stax.WstxOutputFactory 中附带了javax.xml.stream.XMLOutputFactory 的服务提供程序。 我想提供我自己的 javax.xml.stream.XMLOutputFactory 并且在类路径中仍然有woodstox jar。我知道我可以提供自己的系统属性 javax.xml.stream.XMLOutputFactory ,但我正试图从我们的开发运营团队中解脱出来,并使用我的 jar 中的服务文件或战争的 META 中的服务文件来完成-INF/服务文件夹。查看 javax.xml.stream.FactoryFinder 的代码我如何确保我的 META-INF/services/javax.xml.stream.XMLOutputFactory 文件将是 FactoryFinder 使用的文件吗?
我们将 xstream 与骆驼一起使用,但找不到将工厂注入 XStreamDataFormat 的方法
【问题讨论】:
【参考方案1】:如果您的实现在 jar 中,请确保它在类路径上的 woodstox.jar 之前,然后 FactoryFinder 将使用您的实现。
【讨论】:
问题是如何保证它在类路径上的 Woodstox 之前,尤其是当您使用应用容器(例如 Tomcat)时。【参考方案2】:我发现如果我把服务文件放在 WEB-INF/classes/services/javax.xml.stream.XMLOutputFactory 那么它将首先出现在类路径中,然后在 WEB-INF/lib 中的 jar 之前。 这就是我的解决方案。
【讨论】:
我正在解决类似的问题 - 我需要覆盖 Jersey SpringComponentProvider。这个建议对我不起作用,因为 META-INF/services 的路径在 Jersey 内部是硬编码的。我不想投反对票,因为 OP 是关于 XMLOutputFactory 的,但是这个建议不能被视为通用的。WEB-INF/classes/services/javax.xml.stream.XMLOutputFactory
文件的内容是什么?它只是一个类名,还是别的什么?
@vikingsteve 这是一个文件名,应该包含您自己实现的完整类名。【参考方案3】:
首先:我强烈建议您简化生活而不是使用它,而不是依赖 JDK SPI 接口。与自己注入 XMLInputFactory
和/或 XMLOutputFactory
相比,它确实没有任何价值。对于注入,您可以使用 Guice(或 Spring);或者只是手动传递。由于这些工厂没有自己的依赖项,因此这很容易。
但如果选择(或必须)使用XMLInputFactory.newInstance()
,您可以为“javax.xml.stream.XMLOutputFactory”和“javax.xml.stream.XMLInputFactory”定义一个系统属性。
那么为什么不使用 JDK 方法呢?多种原因:
-
它增加了开销:如果您没有指定 System 属性,它必须扫描整个类路径,并且对于大型应用服务器,这需要 10 倍到 100 倍的大部分解析时间
实现的优先级未定义:如果您在类路径中使用多个,您将获得哪一个?谁知道...(请注意:当您在类路径中添加新 jar 时,它甚至可能会改变)
你很可能通过传递依赖获得多个 impl
不幸的是,Oracle 似乎仍然坚持添加这种众所周知的错误方法来注册服务提供商。为什么?可能是因为他们没有自己的 DI 库/框架(Guice 来自 google,Spring 来自 Springsource),而且他们往往非常渴望控制。
【讨论】:
我们将 xstream 与骆驼一起使用,但我没有找到将工厂注入 XStreamDataFormat 的方法。我将代码添加到原始帖子中。但我认为如果我将 META-INF/services 放在我的 WEB-INF/classes 中,是否会首先在类路径中进行扫描。 啊,是的。传递依赖有点讨厌,尤其是。通过两个层次。但是设置系统属性应该可以正常工作——它会比添加 SPI 元数据更快(无类路径扫描)。 为了完整起见,以下是 Javadocs (docs.oracle.com/javase/7/docs/api/javax/xml/stream/…) 所说的:“使用 javax.xml.stream.XMLInputFactory 系统属性。使用 JRE 中的属性文件“lib/stax.properties”目录”【参考方案4】:您可以这样做来指定您要使用的 XMLOutputFactory 实现:
System.setProperty("javax.xml.stream.XMLOutputFactory", ... full classname You want to use ...);
来源: http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/1.6/tutorial/doc/SJSXP4.html
从 JAXP 派生的 XMLInputFactory.newInstance() 方法 确定要加载的具体 XMLInputFactory 实现类 通过使用以下查找过程:
使用 javax.xml.stream.XMLInputFactory 系统属性。 使用 JRE 目录中的 lib/xml.stream.properties 文件。 使用服务 API(如果可用)通过查看 META-INF/services/javax.xml.stream.XMLInputFactory 来确定类名 JRE 可用的 jar 中的文件。 使用平台默认的 XMLInputFactory 实例。
【讨论】:
【参考方案5】:我们遇到了类似的问题,解析将在本地运行但在服务器上失败。调试后发现服务器正在使用阅读器com.ctc.wstx.evt.WstxEventReader
而在本地阅读器上是com.sun.xml.internal.stream.XMLEventReaderImpl
我们设置了以下属性来解决它。
System.setProperty("javax.xml.stream.XMLInputFactory", "com.sun.xml.internal.stream.XMLInputFactoryImpl");
【讨论】:
以上是关于如何在java中覆盖服务提供者的主要内容,如果未能解决你的问题,请参考以下文章