基於CXF Java 搭建Web Service (Restful Web Service與基於SOAP的Web Service混合方案)


快十年沒玩SSH這類東西了,由於上個產品用Restful Service 作為應用服務接口,所以順手整理一下。考慮到當年我剛入門時經歷的一些痛苦,所以這里我會講得詳細些,共享之。

一,選擇一個合適的,Web開發環境:

   我選擇的是Eclipse for J2EE,當然大家可以選擇MyEclipse我只是嫌最新版的MyEclipse Crack太煩,所以沒用它。當年我也是最喜歡它的喲。如果你手頭只有Eclipse for Java沒關系,安裝一個WTP就可以了。

   a.首先創建一個Dynamic Web Project :

 

     在創建的第一頁,Project Name 我用的是"MyServices"中要在Target Runtime中選擇一個容器服務器,我這里測試環境下,選擇的是Tomcat ,如果沒有,可以在右邊點擊“New Runtime"進行新增Tomcat 運行環境。

     在Project創建的最后一頁最好選中創建一個web.xml省得你去創建,缺省情況下,這個web.xml只配置了歡迎頁面。如index.php等。

  b.測試一下,是否這個環境可以發布Build后的代碼及網頁到Tomcat測試環境中運行:

     在左邊的"Web Content"目錄下的”WEB-INF"子目錄,創建一個index.jsp文件進行測試。創建完成后,重新build項目,然后在右下方的Server Tab里會自動列出你剛才選擇的Tomcat發布環境。你點擊這個Tomcat Server

    我的環境是Tomcat Server 7.0 然后右鍵菜單中選擇“Publish" 你會看到 服務器,及服務器下面你的項目右邊會由”Republish"變成“Synchronized" ,說明工程編譯結果都已成功發布到Tomcat Server測試環境下了。

    點擊Tomcat Server 7.0 選擇”Start" 服務啟動,你就可以在你的網頁里輸入“http://localhost:8080/MyServices/index.jsp"你就可以看到你的網頁內容了。

   因為這個網站插圖不方便,有時間再插一些圖示吧。

 

二,關於cxf 框架的運行時序的個人思考過程:

     0.Tomcat啟動時,加載web.xml,根據web.xml的配置,把CXF 的Servlet 放到Tomcat的偵聽端口的映射中,同時也把Spring的Bean加載器也加載到Tomcat的進程空間中,Spring根據初始化的配置文件(比如application-context.xml),加載Bean對象。在加載過程中,會根據配置文件中的xml中的xmlns進行分析(比如,...xmlns:jaxrs ="http://cxf.apache.com", 先到

spring的handlers配置文件中,根據“http://cxf.apache.com"字符串找到對映的handler組件,用這個組件加載所有配置文件中,以jawrs為xml命名空間的配置部分,比如,<jaxrs:server id="services" address="/">     <jaxrs:serviceBeans>       <bean class="com.services.rest.HelloWorld" />     </jaxrs:serviceBeans>     <jaxrs:providers>  可參考:http://blog.csdn.net/javabenface/article/details/7441923)調用對應的加載器進行解釋加載,這里調用cxf的加載器進行加載。cxf加載器會根據 beans.xml中對應的項加載最終實現的class文件,這些class在對應的java源文件編譯過程中,根據java文件中的annomation標記符進行處理,使得這些class加載時形成正確的path 與對象的映射。

     1.客戶端發出請求,通過XML/HTTP把請求及參數對象轉為XML經過HTTP傳到Servlet容器中。

     2.CXF會根據Path與對象的映射找到正確的對象,如果是Restful Web Service還會再根據映射找到Path中對應的執行方法。

三,創建一個基於CXF及Spring的SOAP Web Service:

     1.創建Web Service 相關的類:

       因為這種類型Web Service是SOA(面象服務架構)的,所以是提供一個遠程的RPC接口。所以首先要有一個接口,當然,在服務端要提供真正的服務,所以要有一個這個接口在服務端的實現。下面分別實現:

 IHelloWorld.java:  

package com.services.soap;

import javax.jws.WebParam;

import javax.jws.WebService;

@WebService 

public interface IHelloWorld {

        public String speakoutUserInfo(@WebParam(name = "param") ParamDTO obj);

}

HelloWorld.java:

package com.services.soap;

import javax.jws.WebService;

/**  *   * 使用@WebService指向Interface定義類即可.  */ 

@WebService(endpointInterface = "com.services.soap.IHelloWorld") 

public class HelloWorld implements IHelloWorld{

 @Override

 public String speakoutUserInfo(ParamDTO obj) {

  // TODO Auto-generated method stub

        return "hello";  

}

}

上述的服務實現用到一個對象,這個對象可以做為參數遠程進行傳遞,一般叫做DTO(數據傳輸對象)。當然你可以不用對象,用普通的數據類型這個實例一次性都表現一下。

ParamDTO.java:

package com.services.soap;

import javax.xml.bind.annotation.XmlAccessorType;

import javax.xml.bind.annotation.XmlAccessType;

import javax.xml.bind.annotation.XmlType;

/**  

* Web Service傳輸信息的DTO.  

* 分離entity類與web service接口間的耦合,隔絕entity類的修改對接口的影響. 使用JAXB 2.0的annotation標注JAVA-XML映射,盡量使用默認約定.  *   

*/ 

@XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "User") 

public class ParamDTO  {

        protected Integer id;

        protected String name; 

         public Integer getId()      {

              return id;

        }

        public void setId(Integer value)      {

              id = value;

       }

        public String getName()      {

              return name;

      }

        public void setName(String value)      {

             name = value;

      } 

}

2.在配置文件中體映射這個Service:

我們定義這個Beans.xml

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
    xmlns:jaxws="http://cxf.apache.org/jaxws
    xsi:schemaLocation=" 
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">   
    <jaxws:endpoint id="webServiceHelloWorld" 
        address="/HelloWorld" implementor="com.services.soap.HelloWorld"/> 
</beans> 

這個Beans.xml放到Spring的加載Beans的配置文件中被引用:

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<beans 
    xmlns="http://www.springframework.org/schema/beans
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> 
    <import resource="classpath*:META-INF/cxf/cxf.xml" /> 
    <import resource="classpath*:META-INF/cxf/cxf-extension-soap.xml" /> 
    <import resource="classpath*:META-INF/cxf/cxf-servlet.xml" /> 
    <import resource="classpath:beans.xml" />  //注意這行代碼的引用
   
</beans> 

當然我們要在Web.xml中配置Spring:

<?xml version="1.0" encoding="UTF-8"?>
<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">
  <display-name>webrest</display-name>
 
    <context-param> 
        <param-name>contextConfigLocation</param-name> 
        <param-value>WEB-INF/classes/applicationContext.xml</param-value> 
    </context-param> 
    <listener> 
        <listener-class> 
            org.springframework.web.context.ContextLoaderListener 
        </listener-class> 
    </listener> 
    <servlet> 
        <servlet-name>CXFServlet</servlet-name> 
        <servlet-class> 
            org.apache.cxf.transport.servlet.CXFServlet 
        </servlet-class> 
    </servlet> 
    <servlet-mapping> 
        <servlet-name>CXFServlet</servlet-name> 
        <url-pattern>/*</url-pattern> 
    </servlet-mapping> 
 
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>

也可參考http://www.cnblogs.com/hoojo/archive/2011/03/30/1999563.html提供了另一種相似的配置方式。

四,創建一個基於CXF及Spring的Restful Web Service:

  這個就相對簡單了。因為經不需要直接接供RPC接口給客戶端,只是其於ROA的方式提供資源的操作,可以理解為基於一些xml,json的表達一些資源對象變態變化的傳輸同步給遠程服務。

  所以通過xml映射對象,Annomation進行直接映射方法與path.所以直接寫實現類就行了,當然cxf還有別的框架有其它的映射或配置方式。

 a.代碼實現:

  先上代碼:

 HelloWorld.java

package com.services.rest;

 import javax.ws.rs.Consumes;

 import javax.ws.rs.GET;

 import javax.ws.rs.POST;

 import javax.ws.rs.Path;

 import javax.ws.rs.PathParam;

 import javax.ws.rs.Produces;

 import javax.ws.rs.core.Response;

 import org.codehaus.jackson.jaxrs.JacksonJsonProvider;  

 @Path("/hello")

 public class HelloWorld {

     @GET

     @Path("/echo/{input}")

     @Produces("text/plain")

     public String ping(@PathParam("input") String input) {

         return input + ":in server!";

     }

     @POST

     @Produces("application/json")

     @Consumes("application/json")

     @Path("/jsonBean")

     public Response modifyJson(JsonBean input) {

            input.setCommand(222);

            input.getParam().put("content", "welcome to server!");

           return Response.ok().entity(input).build();

     }

}

其中用到JsonBean對象這個是可以遠程傳送參數對象,一般情況無需特別的定義。就可以直接用了。我這里定義如下:

JsonBean:

package com.services.rest;

import java.util.Map;

public class JsonBean {

 private Integer command;

 private Integer protocolVersion;

 private String  platformType;

 private Map<String, Object> param;

 public Integer getCommand() {

    return command;

 }

 public void setCommand(Integer command) {

     this.command = command;

 }

 public Integer getProtocolVersion() {

     return protocolVersion;

 }

 public void setProtocolVersion(Integer protocolVersion) {

     this.protocolVersion = protocolVersion;

 }

 public String getPlatformType() {

     return platformType;

 }

 public void setPlatformType(String platformType) {

    this.platformType = platformType;

 }

 public Map<String, Object> getParam() {

     return param;

 }

 public void setParam(Map<String, Object> param) {

     this.param = param;

 }

 }

 b.進行發布:

配置一個rest-services.xml進行映射:

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xmlns:jaxrs="http://cxf.apache.org/jaxrs"   xmlns:context="http://www.springframework.org/schema/context"   xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">

  <import resource="classpath:META-INF/cxf/cxf.xml" />

  <context:property-placeholder/>   <context:annotation-config/>   <bean class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer"/>   <bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer"/>

   <jaxrs:server id="services" address="/">     <jaxrs:serviceBeans>       <bean class="com.services.rest.HelloWorld" />     </jaxrs:serviceBeans>     <jaxrs:providers>         <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider"/>     </jaxrs:providers>     </jaxrs:server>

</beans>

在Spring的加載配置文件(applicationContext.xml)里引入:

<?xml version="1.0" encoding="UTF-8"?> 
<beans 
    xmlns="http://www.springframework.org/schema/beans
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> 
    <import resource="classpath*:META-INF/cxf/cxf.xml" /> 
    <import resource="classpath*:META-INF/cxf/cxf-extension-soap.xml" /> 
    <import resource="classpath*:META-INF/cxf/cxf-servlet.xml" />  
    <import resource="classpath:rest-services.xml" />
</beans>

OK,大功告成。

到時此我們能把這兩種模式的Web Service同時在一個框架里發布嗎?當然可以:)要做的只有一步,就是在上面的applicationContext.xml里同時加載兩個Service的映射文件就可以了。

<?xml version="1.0" encoding="UTF-8"?> 
<beans 
    xmlns="http://www.springframework.org/schema/beans
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> 
    <import resource="classpath*:META-INF/cxf/cxf.xml" /> 
    <import resource="classpath*:META-INF/cxf/cxf-extension-soap.xml" /> 
    <import resource="classpath*:META-INF/cxf/cxf-servlet.xml" /> 

    <import resource="classpath:beans.xml" />
    <import resource="classpath:rest-services.xml" />
</beans>

現在就你就可以編譯完成,Publish到你的tomcat上進行測試了,不過一定要注意,在發布選項里一定要把你項目工程中引用的jar依賴庫(比如,cxf相關,spring相關的,Json相關的)同時發布到你的Tomcat Server的運行環境里,這里只需要修改:項目(MyServices)右鍵=》Properties=>Deployment Assembly=>Add=>Java Build Path Entries 不過在引入的jar過多時可能會造成沖突,假如在測試時,說CXF 的一個Discoveryxxx對象..... Null Point之類的錯誤:

SEVERE: Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name
'bookservice': Invocation of init method failed; nested exception is org.apache.cxf.service.factory.ServiceConstructionException
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:
1422)
        .......

        Caused by: org.apache.cxf.service.factory.ServiceConstructionException
        at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:
201)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      
        ...

 Caused by: java.lang.NullPointerException
        at org.apache.cxf.ws.discovery.listeners.WSDiscoveryServerListener.startServer(WSDiscoveryServerListener.java:
64)
        at org.apache.cxf.bus.managers.ServerLifeCycleManagerImpl.startServer(ServerLifeCycleManagerImpl.java:
61)
        at org.apache.cxf.endpoint.ServerImpl.start(ServerImpl.java:
146)
        at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:
192)
          ........

就是最常見的cxf-services-ws-discovery-service-2.x.x.jar沖突,去掉這個.jar的依賴即可。如果你在項目的Java Build Path中去掉這個jar仍不行,就去你測試的Tocat Server上右鍵“clean" 然后再"Publish",如果這樣還不行,說明是Eclipse 清除Tomcat的發布目錄不徹底(Eclipse也有很多bug的),你就去Tomcat 的運行時臨時Web根目錄中去清除這個jar.這個目錄是在Eclipse的Workspace目錄下的”.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps“子目錄。現在估計你能理解為什么你在Eclipse 的runtime server中用Tomcat測試發布后的文件在Tomcat的安裝目錄看不到的原因了吧?呵呵,因為Eclipse整合tomcat測試運行時,根本上會使用自己的臨時目錄作為Tomcat的運行時Web根目錄。

如果你遇到:

Caused by: java.lang.NullPointerException

Class Could not found: org.springframework.web.context.ContextLoaderListener 之類的錯誤。你需要在你的Web project的Deployment Assemblly 中 加入Java build path的庫,即點擊"Add"按鈕,在彈出列表窗口中選擇“Java Build Path Entries"然后選中你的工程發布所需要的庫即可。

到這里應該完成了。

下面就可以用各種客戶端或者瀏覽器進行訪問了,這里主要講方法,可能部分代碼在相關的博文里面附上了:

0.測試工具:

   對於restful web service因為返回的內容都可以簡單的分析,所以可以用很多工具進行測試。

a. 基於firefox的 Poster

b.linux上的curl.

c.......

1.Native 客戶端訪問方法:

  用Java的NIO中的HttpClient就可以搞定 Restful Web Service.

a.cxf的WebClient接口:

 cxf提供了訪問WebService的所有接口,例子代碼如下:

import javax.ws.rs.core.MediaType;
import org.apache.cxf.jaxrs.client.WebClient;
public
class RSETClient {

    private static WebClient client;

    public void init() {
         client = new WebClient("http://localhost:8080/restWeb/hello/teststring");

    }

    public void testGet() {

        System.out.println(client.path("sample").accept(MediaType.TEXT_PLAIN).get(String.class));
  }
}

b.Spring RestTemplate:

這個可以通過在客戶端使用Spring 的RestTemplate 相關的庫來訪問。

 下面代碼是用Spring for Android寫的,PC各平台上調用大同小異,沒時間在這里上代碼了。

 HttpHeaders reqHeader = new HttpHeaders();
  reqHeader.setContentType(new MediaType("text", "plain"));
  HttpEntity<String> req = new HttpEntity<String>(reqHeader);
  String restUrl = "http://192.168.2.100:8080/webrest/hello/echo/testtest";// 這個地址要根據你Restful
                     // 服務器來定
  // 好戲上場了,呵呵
  RestTemplate restTemplate = new RestTemplate(true);
  
  //restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
  ResponseEntity<String> response = restTemplate.exchange(restUrl,
    HttpMethod.GET, req, String.class);
  String msgBody = response.getBody();

 System.out.println(msgBody);

2.瀏覽器測試:

a.可以通過Form進行簡單的訪問。

b. 可以通過客戶端的Ajax代碼來訪問Restful Webservice.

可以通過客戶端的Ajax代碼來訪問Restful Webservice.

3.手機端訪問方法(訪問代碼見我的相關手機客戶端的博文):

a.Android端:

 可以使用Android自帶的HttpClient進行訪問Restful Web Service。這就是Restful Web Service的優勢可以一些平台上最基本的http 庫來訪問。

 可以使用第三方庫KSoap來訪問基於Soap的Web Service.

 同時你可以用Spring for Android 中的RestTemplate接口來訪問 Restful Web service :

HttpHeaders reqHeader = new HttpHeaders();

  reqHeader.setContentType(new MediaType("text", "plain"));

  HttpEntity<String> req = new HttpEntity<String>(reqHeader);

  String restUrl = "http://192.168.2.100:8080/webrest/hello/echo/testtest";// 這個地址要根據你Restful

   // 服務器來定   // 好戲上場了,呵呵

  RestTemplate restTemplate = new RestTemplate(true);

    //restTemplate.getMessageConverters().add(new StringHttpMessageConverter());

  ResponseEntity<String> response = restTemplate.exchange(restUrl,     HttpMethod.GET, req, String.class);

  String msgBody = response.getBody();

  System.out.println(msgBody);

 

b.IOS端:

可以使用IOS上的第三方Http庫來訪問 Restful Web Service,庫名字叫:。

 

 

 


免責聲明!

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



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