SOA专题

使用Apache CXF开发RESTful服务

  开发Web服务有两种方式:

  1. Simple Object Access Protocol (SOAP)
  2. Representational State Transfer (REST)

REST不是技术,也不是某种标准。它仅仅是一种架构风格REST架构的原理是可以通过统一资源标识符或URI唯一标识一个资源。在任何给定的时间点,一个资源的状态是通过文档展现称为代表的资源。客户可以通过请求展现来更新资源的状态。新展现将返回到客户端。资源的接受展现包含的格式有HTML,XML,JSON等。资源遵循REST架构规则被称为一个restfull资源,Web服务遵循这个规则被称为restfull Web服务

那么如何创建一个CXF的Web服务呢?以管理书架上书籍为例子,经过下面步骤:

  1. 增加一本书
  2. 更新书的信息
  3. 从书架上删除一本书
  4. 得到一本书
  5. 得到书的列表
  6. 根据作者得到书籍列表

下面创建的服务如下:

  1. 创建BookVO, BookList (值对象)作为展现传递
  2. 绑定对象到请求和响应
  3. 创建服务实现类接受请求和产生响应。
  4. 注册你的服务到CXF容器
  5. 部署服务到Web容器
  6. 创建客户端调用这个服务

Web服务源码下载:http://subversion.assembla.com/svn/weblog4j/Weblog4jDemo/trunk

客户端代码下载:http://subversion.assembla.com/svn/weblog4j/DemoClient/trunk

下面看一下服务的代码:

package com.aranin.weblog4j.services.rest;

import com.aranin.weblog4j.hashdb.HashDB;
import com.aranin.weblog4j.vo.BookVO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

/**
 * Created by IntelliJ IDEA.
 * User: Niraj Singh
 * Date: 3/13/13
 * Time: 3:58 PM
 * To change this template use File | Settings | File Templates.
 */
public class BookService {

protected final Logger log = LoggerFactory.getLogger(BookService.class);

    @POST
    @Path("/getbook/{name}")
    @Produces({"application/xml","application/json"})
    @Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
    public Response getBucket(@PathParam("name") String name) {
        log.debug("name : " + name);
        BookVO bookVO = null;
        try {
            bookVO = HashDB.getBook(URLDecoder.decode(name, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

        if(bookVO == null){
            return Response.status(Response.Status.BAD_REQUEST).build();
        }else{
            return Response.ok(bookVO).build();
        }
    }

    @POST
    @Path("/addbook")
    @Produces({"application/xml","application/json"})
    @Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
    public Response addBook(@FormParam("name") String bookName,
                            @FormParam("author") String author) {
        log.debug("inside addBook");
        BookVO bookVO = new BookVO();
        bookVO.setBookName(bookName);
        bookVO.setAuthor(author);
        HashDB.insertBook(bookVO);
        if(HashDB.getBook(bookName) == null){
            return Response.status(Response.Status.BAD_REQUEST).build();
        }else{
            return Response.ok(bookVO).build();
        }

    }
}

元注解的几种解释:

  • @POST –服务接受处理POST 请求
  • @Path – web服务的URL路径,抓取URL Url <base_url>/bookservice/getbook/{name} , 增加:<base_url>/bookservice/addbook
  • @Produces – 指示响应的MIME类型,在案例中是 application/xml 和 application/json.
  • @Consumes – 这个服务能消费的请求的MIME类型

注册服务到CXF容器, 在 WEB-INF  创建一个beans.xml,然后跟随Spring启动即可:

<?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"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://cxf.apache.org/jaxrs

http://cxf.apache.org/schemas/jaxrs.xsd

http://cxf.apache.org/jaxws

http://cxf.apache.org/schemas/jaxws.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" />

<jaxws:endpoint
id="bookShelfService"
implementor="com.aranin.weblog4j.services.BookShelfServiceImpl"
address="/bookshelfservice" />

<bean id="bookserviceclass" class="com.aranin.weblog4j.services.rest.BookService"/>

<jaxrs:server id="bookservice" address="/bookservice">
<jaxrs:serviceBeans>
<ref bean="bookserviceclass" />
</jaxrs:serviceBeans>
</jaxrs:server>

</beans>

注册Spring在web.xml:

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/beans.xml,/WEB-INF/applicationContext.xml</param-value>
</context-param>

在web.xml加载 CXFServlet:

<servlet>
<servlet-name>CXFServlet</servlet-name>
<display-name>CXF Servlet</display-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

客户端调用代码:

package com.aranin.weblog4j.client;

import com.aranin.weblog4j.vo.BookVO;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;

import java.net.URLEncoder;

/**
 * Created by IntelliJ IDEA.
 * User: Niraj Singh
 * Date: 3/13/13
 * Time: 4:15 PM
 * To change this template use File | Settings | File Templates.
 */
public class DemoRestClient {
    public static void main(String[] args){
        DemoRestClient restClient = new DemoRestClient();
        try {
            //restClient.addBook("Naked Sun", "Issac Asimov");
            restClient.getBook("Naked Sun");
        } catch (Exception e) {
            e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
        }

    }

    public BookVO getBook(String bookName) throws Exception {

        String output = null;
        try{
            String url = "http://localhost:8080/weblog4jdemo/bookservice/getbook/";

            url = url + URLEncoder.encode(bookName, "UTF-8");

            HttpClient client = new HttpClient();
            PostMethod mPost = new PostMethod(url);
            client.executeMethod( mPost );
            Header mtHeader = new Header();
            mtHeader.setName("content-type");
            mtHeader.setValue("application/x-www-form-urlencoded");
            mtHeader.setName("accept");
            mtHeader.setValue("application/xml");
            mPost.addRequestHeader(mtHeader);
            client.executeMethod(mPost);
            output = mPost.getResponseBodyAsString( );
            mPost.releaseConnection( );
            System.out.println("out : " + output);
        }catch(Exception e){
            throw new Exception("Exception in retriving group page info : " + e);
        }
        return null;
    }

    public void addBook(String bookName, String author) throws Exception {

        String output = null;
        try{
            String url = "http://localhost:8080/weblog4jdemo/bookservice/addbook";
            HttpClient client = new HttpClient();
            PostMethod mPost = new PostMethod(url);
            mPost.addParameter("name", "Naked Sun");
            mPost.addParameter("author", "Issac Asimov");
            Header mtHeader = new Header();
            mtHeader.setName("content-type");
            mtHeader.setValue("application/x-www-form-urlencoded");
            mtHeader.setName("accept");
            mtHeader.setValue("application/xml");
            //mtHeader.setValue("application/json");
            mPost.addRequestHeader(mtHeader);
            client.executeMethod(mPost);
            output = mPost.getResponseBodyAsString( );
            mPost.releaseConnection( );
            System.out.println("output : " + output);
        }catch(Exception e){
        throw new Exception("Exception in adding bucket : " + e);
        }

    }

}

用Jersey开发RESTful服务

使用Apache CXF开发Web服务

使用Spring Webservices建立SOAP服务代理