詳談cxf和axis兩種框架下的webservice客戶端開發(fā)
客戶端相比服務端,基本沒啥配置。引入jar包就好。我這里為了看效果,是新建了maven工程來做客戶端。調用另一個webservice服務端maven工程.
環(huán)境:jdk1.7+maven3.3.9+tomcat7
框架:spring+cxf/spring+axis(這里不是axis2,是axis)
第一種:基于cxf的客戶端開發(fā)
1.引入依賴 pom.xml
這里把兩種框架的依賴一次到位。就不分開引入了。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zhanglf</groupId> <artifactId>cxfClientTest</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>cxfClientTest Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- 添加 Spring dependency --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.1.7.RELEASE</version> </dependency> <!-- 添加CXF dependency --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-core</artifactId> <version>3.1.5</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>3.1.5</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>3.1.5</version> </dependency> <!-- 添加CXF dependency --> <!-- 另一種axis方法調用webservice依賴 --> <dependency> <groupId>org.apache.axis</groupId> <artifactId>axis</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>org.apache.axis</groupId> <artifactId>axis-jaxrpc</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>axis</groupId> <artifactId>axis-wsdl4j</artifactId> <version>1.5.1</version> </dependency> <dependency> <groupId>commons-discovery</groupId> <artifactId>commons-discovery</artifactId> <version>0.2</version> </dependency> <dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.7</version> </dependency> <!-- 另一種axis方法調用webservice --> </dependencies> <build> <finalName>cxfClient</finalName> </build> </project>
2.cxf和axis都沒有spring配置
直接進入業(yè)務代碼CxfClientTest.java
package com.zhanglf; import org.apache.cxf.endpoint.Client; import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory; public class CxfClientTest { public static void main(String[] args) throws Exception { JaxWsDynamicClientFactory factory = JaxWsDynamicClientFactory.newInstance(); //通過wsdl服務描述文件創(chuàng)建客戶端工廠。 Client client = factory.createClient("http://localhost:8080/springCXFWebserviceDemo01/service/HelloWorldService?wsdl"); //嘗試使用不帶?wsdl的地址 //Client client = factory.createClient("http://localhost:8080/springCXFWebserviceDemo01/service/HelloWorldService"); //invoke( //String operationName:要調用的方法名 //Object... params):方法的入參??梢允嵌鄠€。 Object[] objs = client.invoke("sayHello", "阿福"); //invoke方法是默認返回Object[]數組。取出數組的第一位值得值就是返回值。 System.out.println(objs[0].toString()); } }
直接Run As Java Application.可以看到在控制臺打出訪問服務端的代碼。
拓展:這里的客戶端只是傳了一個簡單的人名,正常傳入的是個String類型的xml報文。然后服務端接收報文,進行報文解析,并對信息進行crud操作。并將執(zhí)行結果以報文形式發(fā)送到客戶端??蛻舳嗽谶M行報文解析。判斷執(zhí)行情況以便進行下一步操作。下面我們用axis框架進行稍微接近業(yè)務的代碼開發(fā)。
axis客戶端代碼和cxf都在一個maven工程里。我把工程結構貼出來供參考
第二種:基于axis框架的客戶端開發(fā)
AxisClientTest.java的code,所有涉及的點我都在代碼里說了。解釋的文字比較多,代碼并不多。
package com.zhanglf; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.rpc.ParameterMode; import org.apache.axis.client.Call; import org.apache.axis.client.Service; import org.apache.axis.encoding.XMLType; /** * 說明:address="http://localhost:8080/HelloWorld/services/user?wsdl" * http://localhost:8080/HelloWorld:工程訪問路徑, * /services:此路徑在web.xml里面配置cxf攔截器前置訪問路徑 * /user:此路徑在服務端的applicationContext.xml里配置的,要暴露給外部調用的接口,address:請求路徑 * * @author Administrator * */ public class AxisClientTest { public static void main(String[] args) throws Exception { // namespaceURI // 為:一般可以認為是<wsdl:definitions>中的targetNamespace的值,最好還是<wsdl:definitions>標簽下的 // xmlns:tns的值為準。 // 也是webservice接口的類上注解的targetNamespace,效果如同spring中的requestMapping,用來區(qū)分請求定位到具體的那個接口。 // 這里的命名空間對應的是接口上面的targetNamespace,而不是實現(xiàn)類上面的。故通過wsdl中的<wsdl:definitions // 里面的targetNamespace是不準的,應該以<wsdl:import 中的 // namespace為準或者<wsdl:definitions>標簽下的 xmlns:tns的值為準 String nameSpaceURI = "com.serviceTargetName"; // 指出service所在URL,這個有沒有?wsdl均能正確訪問。。。,這里區(qū)別于cxf,cxf是不能少這個?wsdl String publishUrl = "http://localhost:8080/springCXFWebserviceDemo01/service/HelloWorldService?wsdl"; // 創(chuàng)建一個服務(service)調用(call) Service service = new Service(); // 通過service創(chuàng)建call對象 Call call = (Call) service.createCall(); // 設置webservice接口地址 call.setTargetEndpointAddress(new URL(publishUrl)); // 你需要遠程調用的方法new QName(定位類的targetnamespace,定位方法的方法名) /** * call.setOperationName(new QName("serviceTargetName", "sayHello")); * 方法中的QName方法的入參說明: new QName( String * namespaceURI-定位接口的命名空間:接口注解targetnamespace的值或者wsdl文件 * <wsdl:definitions中的xmlns * :tns="com.serviceTargetName"來鎖定targetnamespace的值, 這里不能用wsdl文件<wsdl: * definitions中的targetNamespace來確定值的原因在于這里的值來源與接口實現(xiàn)類上的targetNamespace注解的值 * 。如果你接口的實現(xiàn)類中的targetNamespace和接口的不一樣,豈不是搞錯了。 String * localPart-接口下定位方法的方法名 * :就是這里的抽象方法sayHello方法名,或者wsdl文件<wsdl:binding標簽下<wsdl:operation * name="sayHello"中name的值。 ) */ call.setOperationName(new QName(nameSpaceURI, "sayHello")); // 方法參數,如果沒有參數請無視。這里如果接口沒有用@WebParam(name = "name"),則會報錯:Unmarshalling // Error: 意外的元素 (uri:"", local:"name")。所需元素為<{}arg0> call.addParameter("parameterName", XMLType.XSD_STRING, ParameterMode.IN); // call.addParameter(new QName(soapaction,"xxxx"), XMLType.XSD_STRING, // ParameterMode.IN); // 設置返回類型,一般用string接收 call.setReturnType(XMLType.XSD_STRING); // 給方法傳遞參數,并且調用方法 String name = "zhanglifeng"; String temp = getXml(name); // 這里的obj{}是放入幾個入參,完全由service提供的接口方法的入參決定,且順序和你存放的順序一致!一般入參為String類型的xml報文,回參也是xml報文。 Object[] obj = new Object[] { temp }; String result = (String) call.invoke(obj); System.out.println(result); } private static String getXml(String name) { StringBuffer sb = new StringBuffer( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); sb.append("<userBean>"); sb.append("<userName>" + name + "</userName>"); sb.append("</userBean>"); return sb.toString(); } }
運行方法:Run As Java Application.客戶端的請求的入參報文如下:
<?xml version="1.0" encoding="UTF-8"?> <userBean> <userName>" + 入參:人名 + </userName> </userBean>
下面我把調用服務端的方法也貼出來,這樣更好理解。昨天已經貼出了服務端的目錄結構,我就把其中的HelloWorldImpl.java中的sayHello方法改一下。code如下:
package com.zlf.impl; import javax.jws.WebService; import org.springframework.stereotype.Component; import org.w3c.dom.Document; import org.w3c.dom.Node; import com.util.XMLUtils; import com.zlf.HelloWorld; /** * 由于實現(xiàn)類和接口不在同一個包中。所以要加上targetNamespace屬性。 * 另外,這里的endpointInterface是實現(xiàn)類對應接口的全路徑 * @author Administrator */ @WebService(targetNamespace="com.serviceTargetName",endpointInterface="com.zlf.HelloWorld") @Component("HelloWord")//spring注入用 public class HelloWorldImpl implements HelloWorld { @Override public String sayHello(String str) { String username="aaa"; Document document = XMLUtils.parse(str); //首先接口開發(fā)肯定是雙發(fā)都知道此方法要接受的報文格式的。我們獲取報文中人名對應的節(jié)點即可。 Node node = document.getElementsByTagName("userName").item(0); if(node !=null){ username=node.getTextContent(); } return "你好,"+username+" 你已成功訪問了webservice服務端!" ; } }
XMLUtils.java工具類我也貼出來,這個也比較常用。
package com.util; import java.io.IOException; import java.io.StringReader; import javax.xml.XMLConstants; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * 解析器 * @author Administrator * */ public class XMLUtils { private final static DocumentBuilder createDocumentBuilder(){ DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); DocumentBuilder builder=null; try { factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); builder=factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } return builder; } public final static Document parse(InputSource in){ try { return createDocumentBuilder().parse(in); } catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public final static Document parse(String xml){ return parse(new InputSource(new StringReader(xml))); } }
這樣就完成了客戶端對服務端的調用。畢竟是初步入門。對以后的開發(fā)還是有所裨益。
一個客戶端和服務端底層傳輸數據的了解
客戶端傳入參數,執(zhí)行String result = (String) call.invoke(new Object[] { “zhanglifeng”})后,實際發(fā)送給服務端的是一個soap底層協(xié)議自動生成的入參報文。
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:com="com.serviceTargetName"> <soapenv:Header/> <soapenv:Body> <com:sayHello> <!--Optional:--> <parameterName>zhanglifeng</parameterName> </com:sayHello> </soapenv:Body> </soapenv:Envelope>
服務端接收這個報文后自動解析,并把入參賦值給方法sayHello(String str)的入參str.經過處理數據返回給客戶端,也是soap底層自動生成的回參報文
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:sayHelloResponse xmlns:ns2="com.serviceTargetName"> <return>你好,zhanglifeng 你已成功訪問了webservice服務端!</return> </ns2:sayHelloResponse> </soap:Body> </soap:Envelope>
客戶端自動把從返回報文中取出返回值,并付值給String result。這樣底層已經處理過了報文。還有一種情況就是如果客戶端傳過來的參數本身就是xml時,底層封裝參數的問題。
如果客戶端的invoke()方法入參
String xml=" <?xml version=\"1.0\" encoding=\"UTF-8\"?><userBean><userName>zhanglifeng</userName></userBean>"
底層自動為xml加上<![CDATA[ xml ]]>:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:com="com.serviceTargetName"> <soapenv:Header/> <soapenv:Body> <com:sayHello> <!--Optional:--> <parameterName><![CDATA[<?xml version="1.0" encoding="UTF-8"?><userBean><userName>zhanglifeng</userName></userBean> ]]></parameterName> </com:sayHello> </soapenv:Body> </soapenv:Envelope>
知道這個原理,在使用SoupUI進行調用webservice接口時就可以使用這種<![CDATA[ 入參xml ]]>格式進行調用。soupui調用的入參如下圖
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
SpringCloud?Alibaba環(huán)境集成之nacos詳解
Spring?Cloud?Alibaba提供了越來越完善的各類微服務治理組件,比如分布式服務配置與注冊中心nacos,服務限流、熔斷組件sentinel等,本篇先來介紹SpringCloud?Alibaba環(huán)境集成之nacos詳解,需要的朋友可以參考下2023-03-03Spring mvc是如何實現(xiàn)與數據庫的前后端的連接操作的?
今天給大家?guī)淼氖顷P于Spring mvc的相關知識,文章圍繞著Spring mvc是如何實現(xiàn)與數據庫的前后端的連接操作的展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下2021-06-06java實現(xiàn)哈弗曼編碼與反編碼實例分享(哈弗曼算法)
本文介紹java實現(xiàn)哈弗曼編碼與反編碼實例,大家參考使用吧2014-01-01