如何在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中覆盖服务提供者的主要内容,如果未能解决你的问题,请参考以下文章

Laravel:覆盖 Bundle 的服务提供者

如何提供模拟实体管理器来测试服务类?

如何实现java服务

如何在Docker下部署nacos并注册Java服务

进博会指定传神提供智能翻译硬件 多举措保障语言服务全覆盖

JAVA springcloud ssm b2b2c多用户商城系统:服务提供与调用