JAX-WS =安装Apache CXF时它"窃取"默认的JDK JAX-WS实现,如何解决?

Mar*_*kus 33 java web-services cxf jax-ws

我有一个奇怪的问题.

  1. 使用wsimport我从WSDL(在专用的eclipse java项目中)生成了JES-WS代码.这在JDK6中工作正常,没有任何外部依赖(在Eclipse中运行)

  2. 我有第二个项目,我曾经使用过Apache CXF.如果我将1.)中描述的代码复制到这个项目中,那么JDK突然不会执行JAX-WS的东西(我生成的文件),而是Apache CXF.

如何防止Apache CXF"运行"JAX-WS的东西.(问题是,CXF无法运行代码......).我也完全不明白Apache CXF如何发现这些类.我没有注册他们吗?

非常感谢你!马库斯

Tom*_*icz 59

Apache CXF(cxf-rt-frontend-jaxws-*.jar确切地说)将自身注册为JVM中的JAX-WS提供程序.在上述JAR中,有一个名为的文件:/META-INF/services/javax.xml.ws.spi.Provider具有以下内容:

org.apache.cxf.jaxws.spi.ProviderImpl
Run Code Online (Sandbox Code Playgroud)

如果您现在查看javax.xml.ws.spi.FactoryFinder#find方法,您将发现JDK在CLASSPATH中搜索javax.xml.ws.spi.Provider文件的存在,如果不可用,则回退到默认的Sun实现.所以你有两个强制回退的选择:

  • 要么cxf-rt-frontend-jaxws-*.jar从CLASSPATH中删除

  • 或覆盖javax.xml.ws.spi.ProviderCXF提供的文件以指向回退位置

第二种选择实际上更容易一些.只需创建:

/src/main/resources/META-INF/services/javax.xml.ws.spi.Provider
Run Code Online (Sandbox Code Playgroud)

文件(假设您正在使用Maven)具有以下内容:

org.apache.cxf.jaxws.spi.ProviderImpl
Run Code Online (Sandbox Code Playgroud)

就是这样,经过测试javax.xml.ws.Endpoint#publish.


小智 16

对于默认实现放置:

com.sun.xml.internal.ws.spi.ProviderImpl
Run Code Online (Sandbox Code Playgroud)

在/src/main/resources/META-INF/services/javax.xml.ws.spi.Provider里面


Epi*_*rce 7

我尝试了另一个,我根本无法使其工作,所以要设置CXF,如果它没有设置为CXF,我只是覆盖服务内的委托.

 try {
        loc = this.getClass().getResource(wsdlResource); 
        QName qName = new QName( wsTargetNamespace, wsName );
        service = new YourWS(loc, qName);
        Field delegateField = Service.class.getDeclaredField("delegate"); //ALLOW CXF SPECIFIC SERVICE DELEGATE ONLY!
        delegateField.setAccessible(true);
        ServiceDelegate previousDelegate = (ServiceDelegate) delegateField.get(service);
        if (!previousDelegate.getClass().getName().contains("cxf")) {
            ServiceDelegate serviceDelegate = ((Provider) Class.forName("org.apache.cxf.jaxws.spi.ProviderImpl").newInstance())
                .createServiceDelegate(loc, qName, service.getClass());
            log.info("The " + getClass().getSimpleName() + " delegate is changed from " + "[" + previousDelegate + "] to [" +
                serviceDelegate +
                "]");
            delegateField.set(service, serviceDelegate);
        }
        port = service.getYourWSSoap();
Run Code Online (Sandbox Code Playgroud)

  • 我尝试了所有的答案,但对于我的应用程序,这是解决我的问题的唯一方法,谢谢:) (2认同)

Par*_*udy 6

标准查找机制在 OSGi (*) 中似乎不能很好地工作。

我有两种方法可以强制服务获取 CXF 实现javax.xml.ws.spi.Provider

  • delegateEpicPandaForce 对此问题的回答中给出的反射设置方法( /sf/answers/2232461381/ )

  • 调用下层JaxWsProxyFactoryBean;这似乎避免了对javax.xml.ws.spi.FactoryFinderJava 包含的所有调用,这是问题的根源

以下是后者的示例,适用于不喜欢反思性地更改私有字段的不那么勇敢的编码人员:

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.getClientFactoryBean().getServiceFactory().setWsdlURL(WinRmService.WSDL_LOCATION);
factory.setServiceName(WinRmService.SERVICE);
factory.setEndpointName(WinRmService.WinRmPort);
// factory.setFeatures(...);  // if required

Service winrm = factory.create(WinRm.class);        

Client client = ClientProxy.getClient(winrm);
Run Code Online (Sandbox Code Playgroud)

一些注意事项:

  • 如果 WSDL 是类路径上的资源,则URL如上所述传递 a ,而不是更简单的factory.setWsdlURL(String)可能需要(避免bundle://...类路径项的无法解析的URL)

  • 您可能需要额外的功能包(例如寻址)


(*)至于为什么查找机制在大多数 OSGi 容器中不起作用,请查看 Oracle Java 中的一点点讨厌FactoryFinder

private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "com.sun.org.glassfish.hk2.osgiresourcelocator.ServiceLoader";

private static boolean isOsgi() {
    try {
        Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME);
        return true;
    } catch (ClassNotFoundException ignored) {
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

OSGi = 玻璃鱼?确实很腥!