为了在一个已有系统中实现对外提供Web Service,并在ESB中注册,使用Apache CXF来实现提供服务的功能。在项目中引入了CXF及其依赖的JAR包并修改web.xml和spring的applicationcontext.xml(CXF和Spring集成的真好)之后,发现原有的调用其它Web Service返回的结果中文出现乱码,并且报文头出现了一些区别。逐步排查发现只要引入JAR包就会出现这个问题,于是乎开始深入研究调用Web Service调用过程来定位问题所在。
调用Web Service服务需要WSDL文件以及根据这个WSDL文件生成的jar包,jar包里除了对外暴露的接口类之外,还有一个继承javax.xml.ws.Service的类和一个包含所有可调用方法的接口类。在使用时通过Class.forName(Service的子类),并通过getConstructor(URL.class, Qname.class).newInstance(XX,XX)
来实例化一个Service类,再通过getPort来获取接口的代理类,进而再调用具体的方法。
在对比分析之后,发现引入CXF包的前后,Service类中的delegate的具体实现类发生了变化。开始怀疑是因为引入的CXF的JAR包通过某种方式覆盖了默认的实现类。
最后还是借助了伟大的面向Stackoverflow编程大法:JAX-WS = When Apache CXF is installed it “steals” default JDK JAX-WS implementation
引用下大牛答案:
Apache CXF (cxf-rt-frontend-jaxws-\*.jar
to be precise) registers itself as a JAX-WS provider in the JVM.
Inside the aforementioned JAR there is a file named: /META-INF/services/javax.xml.ws.spi.Provider
with the following contents:org.apache.cxf.jaxws.spi.ProviderImpl
If you now look at javax.xml.ws.spi.FactoryFinder#find method you will discover that JDK searches the CLASSPATH for the presence of javax.xml.ws.spi.Provider file and falls back to default Sun implementation if not available. So you have two options to force fallback:
- either remove
cxf-rt-frontend-jaxws-\*.jar
from CLASSPATH - or override
javax.xml.ws.spi.Provider
file provided by CXF to point to fallback location
The second option is actually a bit easier. Simply create:/src/main/resources/META-INF/services/javax.xml.ws.spi.Provider
file (assuming you are using Maven) with the following contents:org.apache.cxf.jaxws.spi.ProviderImpl
That’s it, tested with javax.xml.ws.Endpoint#publish
.
在CXF的jar包中有这么个文件/META-INF/services/javax.xml.ws.spi.Provider
(文件名就是javax.xml.ws.spi.Provider),内容只有一行org.apache.cxf.jaxws.spi.ProviderImpl
。
这个时候就涉及到了JAVA SPI(Service Provider Interface)技术,简单来说就是通过配置文件来动态指定实现类。
这篇博客写的非常浅显易懂:Java SPI机制简介
在删除了CXF的JAR包中的javax.xml.ws.spi.Provider文件之后就一切恢复了正常,但是这并没有很好的解决问题,删除之后使用默认的Provider对CXF的功能有什么影响还有待测试,但起码定位了问题,后续继续研究学习!