WebService之JAX-WS、CXF、Spring3.0+


  前言:

          面對工作的需要,web服務這一塊一直都在身邊轉悠着。既然工作中需要這些,作為程序員就應該去了解和學習。下面主要簡述采用CXF+Spring+JAX-WS來發布WebService服務,以及創建客戶端調用服務。

     准備工作:

         1、先了解關於WebService的相關概念以及一些專有名詞的解釋:

     WEBSERVICE 

         W3C的定義是webservice是一個軟件系統,用以支持網絡間不同機器的互動操作。

     受外部環境和實現技術影響,目前普遍認為webservice的技術核心是

     soap,wsdl(一個XML格式文檔,用以描述服務端口訪問方式和使用協議的細節。通常用來輔助生成服務器和客戶端代碼及配置信息),uddi(一個用來發布和搜索WEB服務的協議,應用程序可借由   此協議在設計或運行時找到目標WEB服務)

     這些標准由這些組織制訂:W3C負責XML、SOAP及WSDL;OASIS負責UDDI。

         JAX-WS

    JAX-WS規范是一組XML web services的JAVA API。JAX-WS允許開發者可以選擇RPC-oriented或者message-oriented 來實現自己的web services。

         CXF

         Apache CXF 是一個開源的 Services 框架,主要是幫助開發者來快速構建web服務。

 

   webservice三種最普遍的實現方式是:

   遠程過程調用(RPC)

   面向服務架構(SOA)

   表述性狀態轉移(REST)

      我在這里說的實現方式並不是說 這三種方式是包含於webservice,個人理解它們之間應該是存在交集,即它們是有聯系的同時它們也是有不同的,應該不是從屬的關系。

 

       2、相關的開發環境與依賴的jar()    
          A、CXF官方網址:http://cxf.apache.org/
          B、Jar包下載地址:http://www.apache.org/dyn/closer.cgi?path=/cxf/2.3.3/apache-cxf-2.3.3.zip
           將下來的jar包解壓后,目錄大概就這樣
           bin目錄提供的是常用的dos控制台命令工具
           docs 幫助文檔
           lib jar包、依賴庫
           lib-samples 也是jar包,有jetty、httpclients、junit等jar包
           modules 打包好的WebService模塊
           samples示例demo
          C、源碼下載:http://www.apache.org/dyn/closer.cgi?path=/cxf/2.3.3/apache-cxf-2.3.3-src.zip
            有時候你可以看看源碼,對你了解和學習CXF WebService都會有作用。
          D、CXF的特性
           有代碼生成工具:Java to WSDL;WSDL to Java;XSD to WSDL;WSDL to XML;WSDL to SOAP;WSDL to Service;
           支持 JAX-WS、 JAX-WSA、JSR-181 和 SAAJ;支持 SOAP 1.1、1.2、WS-I BasicProfile、WS-Security、WS-Addressing、WS-RM 和 WS-Policy;支持 WSDL 1.1 、2.0;支持 MTOM;通過 Yoko 支持 CORBA;通過 Tuscany 支持 SCA;通過 ServiceMix 支持 JBI;內置Jetty應用服務器(Jetty服務器也是當今web開發的一款比較好用的服務器);

 

      開發:

         開發需要的jar如下:

         

              1、首先創建一個web項目,將得到的包拷貝到lib目錄下

               既然是想發布自己的服務,首先創建一個接口

IComplexUserService
 1 package com.chh.service;
 2 
 3 import javax.jws.WebParam;
 4 import javax.jws.WebService;
 5 import javax.jws.soap.SOAPBinding;
 6 import javax.jws.soap.SOAPBinding.Style;
 7 import com.chh.entity.User;
 8 
 9 /**
10  * 定制客戶端請求WebService所需要的接口
11  * @author chh
12  *
13  */
14 @WebService
15 @SOAPBinding(style=Style.RPC)
16 public interface IComplexUserService {
17     
18     public User getUserByName(@WebParam(name = "name") String name);
19 
20     public void setUser(User user);
21 
22 }

           接口只是用來定義,具體操作要在它下面的實現類中得以體現

ComplexUserService
 1 package com.chh.service;
 2 
 3 import java.util.Date;
 4 
 5 import javax.jws.WebParam;
 6 import javax.jws.WebService;
 7 import javax.jws.soap.SOAPBinding;
 8 import javax.jws.soap.SOAPBinding.Style;
 9 
10 import com.chh.entity.User;
11 /**
12  * WebService傳遞復雜對象,如JavaBean、Array、List、Map等
13  * @author chh
14  *
15  */
16 @WebService
17 @SOAPBinding(style=Style.RPC)
18 @SuppressWarnings("deprecation")
19 public class ComplexUserService implements IComplexUserService {
20 
21     @Override
22     public User getUserByName(@WebParam(name = "name") String name) {
23         User user = new User();
24 
25         user.setId(new Date().getSeconds());
26         user.setName(name);
27         user.setAddress("china");
28         user.setEmail(name + "@hoo.com");
29 
30         return user;
31     }
32 
33     @Override
34     public void setUser(User user) {
35         System.out.println("############Server setUser###########");
36         System.out.println("setUser:" + user);
37     }
38 
39 }

          實體

User
 1 package com.chh.entity;
 2 
 3 import java.io.Serializable;
 4 /**
 5  * 序列化user實體
 6  * @author chh
 7  * 
 8  */
 9 public class User implements Serializable {
10 
11     private static final long serialVersionUID = 677484458789332877L;
12     private int id;
13     private String name;
14     private String email;
15     private String address;
16 
17     public int getId() {
18         return id;
19     }
20 
21     public void setId(int id) {
22         this.id = id;
23     }
24 
25     public String getName() {
26         return name;
27     }
28 
29     public void setName(String name) {
30         this.name = name;
31     }
32 
33     public String getEmail() {
34         return email;
35     }
36 
37     public void setEmail(String email) {
38         this.email = email;
39     }
40 
41     public String getAddress() {
42         return address;
43     }
44 
45     public void setAddress(String address) {
46         this.address = address;
47     }
48 
49     public static long getSerialversionuid() {
50         return serialVersionUID;
51     }
52     
53     @Override
54     public String toString() {
55         return this.id + "#" + this.name + "#" + this.email + "#" + this.address;
56     }
57 
58 }
Users
 1 package com.chh.entity;
 2 
 3 import java.util.HashMap;
 4 import java.util.List;
 5 
 6 /**
 7  * 
 8  * @author chh
 9  * 
10  */
11 public class Users {
12     private List<User> users;
13     private User[] userArr;
14     private HashMap<String, User> map;
15 
16     public List<User> getUsers() {
17         return users;
18     }
19 
20     public void setUsers(List<User> users) {
21         this.users = users;
22     }
23 
24     public User[] getUserArr() {
25         return userArr;
26     }
27 
28     public void setUserArr(User[] userArr) {
29         this.userArr = userArr;
30     }
31 
32     public HashMap<String, User> getMap() {
33         return map;
34     }
35 
36     public void setMap(HashMap<String, User> map) {
37         this.map = map;
38     }
39 
40 }

         自定義消息攔截器(如果在你的項目中不需要用到攔截器的時候,可以撤掉不用)

MessageInterceptor
 1 package com.chh.interceptor;
 2 
 3 import org.apache.cxf.interceptor.Fault;
 4 import org.apache.cxf.message.Message;
 5 import org.apache.cxf.phase.AbstractPhaseInterceptor;
 6 
 7 /**
 8  * 自定義消息攔截器
 9  * @author chh
10  *
11  */
12 public class MessageInterceptor extends AbstractPhaseInterceptor<Message> {
13 
14     //至少要有一個帶參的構造方法
15     public MessageInterceptor(String phase) {
16         super(phase);
17     }
18     
19     public void handleMessage(Message message) throws Fault {
20         System.out.println("############handleMessage##########");
21         System.out.println(message);
22         
23         if(message.getDestination() != null){
24              System.out.println(message.getId() + "#" + message.getDestination().getMessageObserver());
25         }
26         
27         if(message.getExchange() != null){
28             System.out.println(message.getExchange().getInMessage() + "#" + message.getExchange().getInFaultMessage());
29             System.out.println(message.getExchange().getOutMessage() + "#" + message.getExchange().getOutFaultMessage());
30         }
31         
32     }
33 
34 }

         用xml配置服務端(applicationContext-service.xml)

applicationContext-server.xml
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:context="http://www.springframework.org/schema/context"
 4     xmlns:jaxws="http://cxf.apache.org/jaxws"
 5     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 6     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 7     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 8     http://www.springframework.org/schema/context
 9     http://www.springframework.org/schema/context/spring-context-3.0.xsd
10     http://cxf.apache.org/jaxws 
11     http://cxf.apache.org/schemas/jaxws.xsd">
12     
13     <import resource="classpath:META-INF/cxf/cxf.xml"/>
14     <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
15     <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
16     
17     <bean id="userServiceBean" class="com.chh.service.ComplexUserService"/>
18 
19     <bean id="inMessageInterceptor" class="com.chh.interceptor.MessageInterceptor">
20         <constructor-arg  value="receive"/>
21     </bean>
22 
23     <bean id="outLoggingInterceptor" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
24     
25     <!-- 注意下面的address,這里的address的名稱就是訪問的WebService的name -->
26     <jaxws:server id="userService" serviceBean="#userServiceBean" address="/Users">
27         <jaxws:inInterceptors>
28             <ref bean="inMessageInterceptor"/>
29         </jaxws:inInterceptors>
30     
31         <jaxws:outInterceptors>
32             <ref bean="outLoggingInterceptor"/>
33         </jaxws:outInterceptors>
34     </jaxws:server>
35     
36 </beans>

        注意於此同時web.xml中要加在Spring容器

web.xml
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
 3   <display-name>CXFWebService</display-name>
 4       
 5   <!-- 加載Spring容器配置 -->
 6     <listener>
 7         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 8     </listener>
 9     
10     <!-- 設置Spring容器加載配置文件路徑 -->
11     <context-param>
12         <param-name>contextConfigLocation</param-name>
13         <param-value>classpath*:applicationContext-server.xml</param-value>
14     </context-param>
15     
16     <listener>
17         <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
18     </listener>
19 
20     <servlet>
21         <servlet-name>CXFService</servlet-name>
22         <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
23     </servlet>
24 
25     <servlet-mapping>
26         <servlet-name>CXFService</servlet-name>
27         <url-pattern>/*</url-pattern>
28     </servlet-mapping>    
29 </web-app>

到這一步為止,我們web服務端開發完成!可以啟動項目測試一下自己的web服務有沒有發布成功,其中成功與否第一要看項目啟動時是否報錯,如果報錯看具體報什么錯誤,是包沖突,還是包的版本太低等。啟動項目不報錯,那么輸入地址查看你的這個web服務的wsdl信息:http://localhost:8080/CXFWebService/Users,如果能夠看到看到xml格式的信息,則說明你成功發布了web服務。

有了web服務端,但是如果我在程序中想通過別人提供的web服務地址得到自己想要的數據,這一步又該怎么做呢?下面將繼續采用CXF+Spring3.0+ 定制開發web服務客戶端。其實到這一步已經很簡單了,我們可以通過wsdl中的信息來定制客戶端(具體有怎樣的規則與細節,可以參看http://blog.csdn.net/qjyong/article/details/2148558   這篇博客下面的幾個段落)。

       根據服務端定制客戶端

applicationContext-client.xml
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3 
 4     xmlns:context="http://www.springframework.org/schema/context"
 5     xmlns:jaxws="http://cxf.apache.org/jaxws"
 6     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 7     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 8     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
 9     http://www.springframework.org/schema/context
10     http://www.springframework.org/schema/context/spring-context-3.0.xsd
11     http://cxf.apache.org/jaxws 
12     http://cxf.apache.org/schemas/jaxws.xsd">
13 
14     <import resource="classpath:META-INF/cxf/cxf.xml"/>
15     <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
16     <import resource="classpath:META-INF/cxf/cxf-servlet.xml"/>
17 
18     <!-- serviceClass寫定制客戶接口的全路徑 -->
19     <jaxws:client id="userWsClient" serviceClass="com.chh.service.IComplexUserService" 
20         address="http://localhost:8080/CXFWebService/Users"/>
21     
22 </beans>

      利用客戶端得到服務端的數據。值得說的是,以前發布WebService是用Endpoint的push方法。這里用的是JaxWsServerFactoryBean和客戶端調用的代碼JaxWsProxyFactoryBean有點不同。

SpringUsersWsClient
 1 package com.chh.client;
 2 
 3 import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
 4 import org.springframework.context.ApplicationContext;
 5 import org.springframework.context.support.ClassPathXmlApplicationContext;
 6 
 7 import com.chh.entity.User;
 8 import com.chh.service.IComplexUserService;
 9 
10 /**
11  * 請求Spring整合CXF的WebService客戶端
12  * @author chh
13  *
14  */
15 public class SpringUsersWsClient {
16     
17     public static void clientFirst(){
18         JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
19         factory.setServiceClass(IComplexUserService.class);
20         factory.setAddress("http://localhost:8080/CXFWebService/Users");
21         
22         IComplexUserService service = (IComplexUserService) factory.create();
23         
24         System.out.println("#############Client getUserByName##############");
25         User user = service.getUserByName("chh");
26         System.out.println(user);
27 
28         user.setAddress("China-Guangzhou");
29         service.setUser(user);
30     }
31     
32     public static void clientToXML(){
33         ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-client.xml");
34 
35         IComplexUserService service = ctx.getBean("userWsClient", IComplexUserService.class);
36         
37         System.out.println("#############Client getUserByName##############");
38         User user = service.getUserByName("chh");
39         System.out.println(user);
40 
41         user.setAddress("China-Guangzhou");
42         service.setUser(user);
43     }
44 
45     public static void main(String[] args) {
46         clientToXML();
47     }
48 }

      這樣下來,只要項目還啟動着web服務還沒有停,我們可以運行SpringUserWsClient這個文件的main方法就可以調用到服務端的數據。

    項目目錄結構圖:

   

整個服務的發布與調用都是采用xml形式開發的,其實如果你不習慣用的話可以文件中用代碼實現,這樣也是可以的。

到這里,讓我想起了公司里面的那套框架的發布服務與調用服務,如果一個web服務想要加密以及MAC校驗、設置訪問的最大數、超時時間、描述等等,采用現在

這種發布服務的方式那該如何實現,客戶端又該如何調用?或者說有沒有更加簡單的方式來實現這一切功能呢?這樣的一些想法還都在摸索着前進,如果有那位熱

心腸有高見的話,希望大家知識共享!

     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM