背景:java
有個項目, 須要由第三方提供用戶信息, 實現用戶同步操做, 對方給提供webservice接口(axis2實現)並也使用axis2做主客戶端調用我方提供的webservice接口web
起初, 因爲項目使用了spring, 且spring可與cxf較好的集成, 因此也就選用了cxf,spring
可問題隨之出現, 接口能夠調用到, 接口的具體方法也能夠調用到,apache
可是,tomcat
1. cxf做爲客戶端, 獲取服務端返回值時均爲null.服務器
2. cxf做爲服務端, 獲取axis2客戶端傳來的參數時, 也均爲null.app
針對上述問題, 作了大量調試模擬, 若有不妥之處, 敬請指正.frontend
一. axis2服務器搭建eclipse
簡單起見, axis2r搭建採用較爲簡單的一種方式, 即將服務類和services.xml打成.aar包發佈.webapp
1. 下載部署axis2
http://axis.apache.org/axis2/java/core/
這裏選擇下載的1.7.0版本, axis2-1.7.0-war.zip
2. 將zip文件中的axis2.war包解壓到tomcat的webapps目錄中, 啓動tomcat,
完成axis2的安裝部署, 以下圖:
3. 訪問 http://localhost/axis2 顯示以下頁面, 表示axis2部署成功
4. 訪問 http://localhost/axis2/services/listServices , 可查看此axis2所發佈的webservice服務, 以下圖:
其中, Version爲axis2默認發佈的服務, getVersion是此服務的方法
二. 編寫發佈webserivce接口
1. 新建java項目myAxis2
2. 建立服務類HelloShooter.java
1 package com.shooter.webservice; 2 3 public class HelloShooter { 4 5 public void getShooterId(String shooterId) { 6 System.out.println("狙擊手編號: " + shooterId); 7 } 8 9 public String shoot(int num) { 10 return "本次出擊共狙擊 " + num + " 名敵軍"; 11 } 12 13 public String undershoot() { 14 return "脫靶, 很遺憾!"; 15 } 16 17 }
3. 新建META-INF目錄, 並建立services.xml文件
services.xml源碼以下:
1 <serviceGroup> 2 <!-- 第一個webservice服務 --> 3 <service name="HelloShooter" targetNamespace="http://sharp-shooter"> 4 <!-- 命名空間 --> 5 <schema schemaNamespace="http://sharp-shooter" /> 6 <!-- 發佈的服務類全路徑 --> 7 <parameter name="ServiceClass">com.shooter.webservice.HelloShooter 8 </parameter> 9 <!-- 對每一個方法配置處理器 --> 10 <operation name="getShooterId"> 11 <messageReceiver class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver" /> 12 </operation> 13 <operation name="shoot"> 14 <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" /> 15 </operation> 16 <operation name="undershoot"> 17 <messageReceiver class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" /> 18 </operation> 19 </service> 20 </serviceGroup>
另外一種services.xml編寫方式, 配置全局處理器, 但此種方法我沒有成功過, 如哪位測試成功了, 交流一下
1 <serviceGroup> 2 <!-- 第一個webservice服務 --> 3 <service name="HelloShooter" targetNamespace="http://sharp-shooter.com"> 4 <!-- 命名空間 --> 5 <schema schemaNamespace="http://sharp-shooter.com" /> 6 <!-- 發佈的服務類全路徑 --> 7 <parameter name="ServiceClass">com.shooter.webservice.HelloShooter</parameter> 8 <messageReceivers> 9 <!--有返回值的處理器--> 10 <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out" 11 class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" /> 12 <!--無返回值的處理器--> 13 <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-only" 14 class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver" /> 15 </messageReceivers> 16 </service> 17 </serviceGroup>
其中,
1) service元素的name屬性爲服務名稱, 若是沒有設置此屬性, 那麼服務名稱爲發佈包名, 如發佈包爲shooter.aar, 則服務名爲shooter
2) schema配置的爲命名空間, 並在service元素中配置一樣的命名空間
3) parameter name="ServicesClass"配置的爲服務類的全路徑
4) 處理器, 第一種方式爲全局處理器, 第二各方式爲爲某個方法配置所須要的處理器
4. 建立.aar包
1) 使用myeclipse的導出功能, 導出編寫的服務類和services.xml文件爲jar包, 如圖:
可去除沒必要要的文件.
2) 修改包擴展名爲.aar
3) 將.aar包拷貝到...\apache-tomcat-6.0.35-80\webapps\axis2\WEB-INF\services目錄中(自動部署), 完成服務端發佈
4) 訪問 http://localhost/axis2/services/listServices , 服務頁面顯示HelloShooter服務, 則發佈成功
三. CXF客戶端
1. 新建項目, 引入所需CXF的maven依賴(此處有坑, 後文解釋)
1 <dependency> 2 <groupId>org.apache.cxf</groupId> 3 <artifactId>cxf-rt-frontend-jaxws</artifactId> 4 <version>2.2.9</version> 5 </dependency> 6 <dependency> 7 <groupId>org.apache.cxf</groupId> 8 <artifactId>cxf-rt-core</artifactId> 9 <version>2.2.9</version> 10 </dependency> 11 <dependency> 12 <groupId>org.apache.cxf</groupId> 13 <artifactId>cxf-rt-transports-http</artifactId> 14 <version>2.2.9</version> 15 </dependency>
2. 獲取HelloShooter的接口信息
訪問 http://localhost/axis2/services/HelloShooter?wsdl, 獲取以下信息:
1 <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 2 xmlns:ns1="http://org.apache.axis2/xsd" xmlns:ns="http://sharp-shooter" 3 xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 4 xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 5 xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" 6 targetNamespace="http://sharp-shooter"> 7 <wsdl:documentation>HelloShooter</wsdl:documentation> 8 <wsdl:types> 9 <xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://sharp-shooter"> 10 <xs:element name="undershoot"> 11 <xs:complexType> 12 <xs:sequence /> 13 </xs:complexType> 14 </xs:element> 15 <xs:element name="undershootResponse"> 16 <xs:complexType> 17 <xs:sequence> 18 <xs:element minOccurs="0" name="return" nillable="true" type="xs:string" /> 19 </xs:sequence> 20 </xs:complexType> 21 </xs:element> 22 <xs:element name="shoot"> 23 <xs:complexType> 24 <xs:sequence> 25 <xs:element name="num" type="xs:int" /> 26 </xs:sequence> 27 </xs:complexType> 28 </xs:element> 29 <xs:element name="shootResponse"> 30 <xs:complexType> 31 <xs:sequence> 32 <xs:element minOccurs="0" name="return" nillable="true" type="xs:string" /> 33 </xs:sequence> 34 </xs:complexType> 35 </xs:element> 36 <xs:element name="getShooterId"> 37 <xs:complexType> 38 <xs:sequence> 39 <xs:element minOccurs="0" name="shooterId" nillable="true" type="xs:string" /> 40 </xs:sequence> 41 </xs:complexType> 42 </xs:element> 43 </xs:schema> 44 </wsdl:types> 45 <wsdl:message name="undershootRequest"> 46 <wsdl:part name="parameters" element="ns:undershoot" /> 47 </wsdl:message> 48 <wsdl:message name="undershootResponse"> 49 <wsdl:part name="parameters" element="ns:undershootResponse" /> 50 </wsdl:message> 51 <wsdl:message name="shootRequest"> 52 <wsdl:part name="parameters" element="ns:shoot" /> 53 </wsdl:message> 54 <wsdl:message name="shootResponse"> 55 <wsdl:part name="parameters" element="ns:shootResponse" /> 56 </wsdl:message> 57 <wsdl:message name="getShooterIdRequest"> 58 <wsdl:part name="parameters" element="ns:getShooterId" /> 59 </wsdl:message> 60 <wsdl:message name="getShooterIdResponse" /> 61 <wsdl:portType name="HelloShooterPortType"> 62 <wsdl:operation name="undershoot"> 63 <wsdl:input message="ns:undershootRequest" wsaw:Action="urn:undershoot" /> 64 <wsdl:output message="ns:undershootResponse" wsaw:Action="urn:undershootResponse" /> 65 </wsdl:operation> 66 <wsdl:operation name="shoot"> 67 <wsdl:input message="ns:shootRequest" wsaw:Action="urn:shoot" /> 68 <wsdl:output message="ns:shootResponse" wsaw:Action="urn:shootResponse" /> 69 </wsdl:operation> 70 <wsdl:operation name="getShooterId"> 71 <wsdl:input message="ns:getShooterIdRequest" wsaw:Action="urn:getShooterId" /> 72 <wsdl:output message="ns:getShooterIdResponse" wsaw:Action="urn:getShooterIdResponse" /> 73 </wsdl:operation> 74 </wsdl:portType> 75 <wsdl:binding name="HelloShooterSoap11Binding" type="ns:HelloShooterPortType"> 76 <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> 77 <wsdl:operation name="undershoot"> 78 <soap:operation soapAction="urn:undershoot" style="document" /> 79 <wsdl:input> 80 <soap:body use="literal" /> 81 </wsdl:input> 82 <wsdl:output> 83 <soap:body use="literal" /> 84 </wsdl:output> 85 </wsdl:operation> 86 <wsdl:operation name="shoot"> 87 <soap:operation soapAction="urn:shoot" style="document" /> 88 <wsdl:input> 89 <soap:body use="literal" /> 90 </wsdl:input> 91 <wsdl:output> 92 <soap:body use="literal" /> 93 </wsdl:output> 94 </wsdl:operation> 95 <wsdl:operation name="getShooterId"> 96 <soap:operation soapAction="urn:getShooterId" style="document" /> 97 <wsdl:input> 98 <soap:body use="literal" /> 99 </wsdl:input> 100 <wsdl:output> 101 <soap:body use="literal" /> 102 </wsdl:output> 103 </wsdl:operation> 104 </wsdl:binding> 105 <wsdl:binding name="HelloShooterSoap12Binding" type="ns:HelloShooterPortType"> 106 <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" /> 107 <wsdl:operation name="undershoot"> 108 <soap12:operation soapAction="urn:undershoot" style="document" /> 109 <wsdl:input> 110 <soap12:body use="literal" /> 111 </wsdl:input> 112 <wsdl:output> 113 <soap12:body use="literal" /> 114 </wsdl:output> 115 </wsdl:operation> 116 <wsdl:operation name="shoot"> 117 <soap12:operation soapAction="urn:shoot" style="document" /> 118 <wsdl:input> 119 <soap12:body use="literal" /> 120 </wsdl:input> 121 <wsdl:output> 122 <soap12:body use="literal" /> 123 </wsdl:output> 124 </wsdl:operation> 125 <wsdl:operation name="getShooterId"> 126 <soap12:operation soapAction="urn:getShooterId" style="document" /> 127 <wsdl:input> 128 <soap12:body use="literal" /> 129 </wsdl:input> 130 <wsdl:output> 131 <soap12:body use="literal" /> 132 </wsdl:output> 133 </wsdl:operation> 134 </wsdl:binding> 135 <wsdl:binding name="HelloShooterHttpBinding" type="ns:HelloShooterPortType"> 136 <http:binding verb="POST" /> 137 <wsdl:operation name="undershoot"> 138 <http:operation location="undershoot" /> 139 <wsdl:input> 140 <mime:content type="application/xml" part="parameters" /> 141 </wsdl:input> 142 <wsdl:output> 143 <mime:content type="application/xml" part="parameters" /> 144 </wsdl:output> 145 </wsdl:operation> 146 <wsdl:operation name="shoot"> 147 <http:operation location="shoot" /> 148 <wsdl:input> 149 <mime:content type="application/xml" part="parameters" /> 150 </wsdl:input> 151 <wsdl:output> 152 <mime:content type="application/xml" part="parameters" /> 153 </wsdl:output> 154 </wsdl:operation> 155 <wsdl:operation name="getShooterId"> 156 <http:operation location="getShooterId" /> 157 <wsdl:input> 158 <mime:content type="application/xml" part="parameters" /> 159 </wsdl:input> 160 <wsdl:output> 161 <mime:content type="application/xml" part="parameters" /> 162 </wsdl:output> 163 </wsdl:operation> 164 </wsdl:binding> 165 <wsdl:service name="HelloShooter"> 166 <wsdl:port name="HelloShooterHttpSoap11Endpoint" binding="ns:HelloShooterSoap11Binding"> 167 <soap:address location="http://localhost/axis2/services/HelloShooter.HelloShooterHttpSoap11Endpoint/" /> 168 </wsdl:port> 169 <wsdl:port name="HelloShooterHttpSoap12Endpoint" binding="ns:HelloShooterSoap12Binding"> 170 <soap12:address location="http://localhost/axis2/services/HelloShooter.HelloShooterHttpSoap12Endpoint/" /> 171 </wsdl:port> 172 <wsdl:port name="HelloShooterHttpEndpoint" binding="ns:HelloShooterHttpBinding"> 173 <http:address location="http://localhost/axis2/services/HelloShooter.HelloShooterHttpEndpoint/" /> 174 </wsdl:port> 175 </wsdl:service> 176 </wsdl:definitions>
3. 根據接口信息, 建立客戶端接口
1 package com.shooter.cxf.client; 2 3 import javax.jws.WebMethod; 4 import javax.jws.WebParam; 5 import javax.jws.WebResult; 6 import javax.jws.WebService; 7 8 @WebService(name="HelloShooter", targetNamespace="http://sharp-shooter") 9 public interface IHelloShooter { 10 11 @WebMethod(operationName="getShooterId") 12 public void getShooterId(@WebParam(name="shooterId", targetNamespace="http://sharp-shooter")String shooterId); 13 14 @WebMethod(operationName="shoot") 15 public @WebResult(targetNamespace="http://sharp-shooter") String shoot(@WebParam(name="num", targetNamespace="http://sharp-shooter")int num); 16 17 @WebMethod(operationName="undershoot") 18 public @WebResult(targetNamespace="http://sharp-shooter") String undershoot(); 19 20 }
注意重點: 此接口主要在4處添加了註解, 分別是: 類, 方法, 方法參數, 方法返回值,
前面描述的項目中出現的問題, 也正在這裏某一處或幾處沒有添加註解引發的(說實話, 感受有點怪怪的, 哪位大神有更好的辦法, 求賜教)
4. 建立接口調用測試類
1 package com.shooter.cxf.client; 2 3 import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; 4 5 public class CXFClient { 6 7 public static void main(String[] args) { 8 // 保存返回值 9 String result = ""; 10 11 String url = "http://localhost/axis2/services/HelloShooter"; 12 JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); 13 factory.setServiceClass(IHelloShooter.class); 14 factory.setAddress(url); 15 // 建立接口代理對象 16 IHelloShooter helloServices = (IHelloShooter) factory.create(); 17 18 // 調用接口方法 19 helloServices.getShooterId("1123"); 20 21 System.out.println("-----------------------------"); 22 result = helloServices.shoot(6); 23 System.out.println(result); 24 25 System.out.println("-----------------------------"); 26 result = helloServices.undershoot(); 27 System.out.println(result); 28 } 29 }
執行main方法:
1) getShooterId()方法, 無返回值, 服務端打印相關信息
2) shoot()和undershoot()方法, 有返回值, 在客戶端打印相關信息
運行不粗來? 客戶端報異常? getShooterId()方法? 那就對了...
填坑:
調試的時候, 試驗N屢次, 客戶端總報以下異常信息:
1 Exception in thread "main" org.apache.cxf.binding.soap.SoapFault: Error reading XMLStreamReader. 2 at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:238) 3 at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:60) 4 at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263) 5 at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:801) 6 at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1679) 7 at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1517) 8 at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1425) 9 at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56) 10 at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:650) 11 at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62) 12 at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263) 13 at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:531) 14 at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:462) 15 at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:365) 16 at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:318) 17 at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:338) 18 at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:324) 19 at com.jialin.ejb.MyClient.main(MyClient.java:40) 20 Caused by: com.ctc.wstx.exc.WstxEOFException: Unexpected EOF in prolog 21 at [row,col {unknown-source}]: [1,0] 22 at com.ctc.wstx.sr.StreamScanner.throwUnexpectedEOF(StreamScanner.java:677) 23 at com.ctc.wstx.sr.BasicStreamReader.handleEOF(BasicStreamReader.java:2116) 24 at com.ctc.wstx.sr.BasicStreamReader.nextFromProlog(BasicStreamReader.java:2022) 25 at com.ctc.wstx.sr.BasicStreamReader.next(BasicStreamReader.java:1114) 26 at com.ctc.wstx.sr.BasicStreamReader.nextTag(BasicStreamReader.java:1137) 27 at org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor.handleMessage(ReadHeadersInterceptor.java:139) 28 ... 17 more
提煉一下, 就倆信息: Error reading XMLStreamReader.和[row,col {unknown-source}]: [1,0]
客戶端出問題(固然也不能這麼說, 當時的第一感受)
各類試, 最後換了CXF的版本, 問題解決了
1 <dependency> 2 <groupId>org.apache.cxf</groupId> 3 <artifactId>cxf-rt-frontend-jaxws</artifactId> 4 <version>2.7.16</version> 5 </dependency> 6 <dependency> 7 <groupId>org.apache.cxf</groupId> 8 <artifactId>cxf-rt-core</artifactId> 9 <version>2.7.16</version> 10 </dependency> 11 <dependency> 12 <groupId>org.apache.cxf</groupId> 13 <artifactId>cxf-rt-transports-http</artifactId> 14 <version>2.7.16</version> 15 </dependency>
網上好像有帖子說服務端的問題, 我的以爲也很差這樣說,
暫且說服務端與客戶端版本不匹配吧,
若是有更好的解釋, 不吝賜教