zoukankan      html  css  js  c++  java
  • Apache CXF实现Web Service(2)——不借助重量级Web容器和Spring实现一个纯的JAX-RS(RESTful) web service

    实现目标

    http://localhost:9000/rs/roomservice 为入口, 
    http://localhost:9000/rs/roomservice/room为房间列表, 
    http://localhost:9000/rs/roomservice/room/001/ 为001号房间的信息, 
    http://localhost:9000/rs/roomservice/room/001/person 为在001号房间主的人的列表 

    在Eclipse中新建一个Java Project

    (可以不是WTP的Dynamic Web Project)参考文章:Apache CXF实现Web Service(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.cnblog.richaaaard.cxfstudy</groupId>  
        <artifactId>cxf-test-standalone-rs-helloworld</artifactId>  
        <packaging>war</packaging>  
        <version>1.0-SNAPSHOT</version>  
        <name>cxf-test-standalone-rs-helloworld Maven Webapp</name>  
        <url>http://maven.apache.org</url>  
          
        <properties>  
    <!--         <cxf.version>2.7.18</cxf.version> -->
            <cxf.version>3.1.4</cxf.version>        
        </properties>  
          
        <dependencies>
            <dependency>  
                <groupId>org.apache.cxf</groupId>  
                <artifactId>cxf-rt-transports-http</artifactId>  
                <version>${cxf.version}</version>  
            </dependency>  
            <dependency>  
                <groupId>org.apache.cxf</groupId>  
                <artifactId>cxf-rt-transports-http-jetty</artifactId>  
                <version>${cxf.version}</version>  
            </dependency>  
            <dependency>  
                <groupId>org.apache.cxf</groupId>  
                <artifactId>cxf-rt-ws-security</artifactId>  
                <version>${cxf.version}</version>  
            </dependency>  
            <dependency>  
                <groupId>org.apache.cxf</groupId>  
                <artifactId>cxf-rt-ws-policy</artifactId>  
                <version>${cxf.version}</version>  
            </dependency>  
    <!--         <dependency>   -->
    <!--             <groupId>org.apache.cxf</groupId>   -->
    <!--             <artifactId>cxf-bundle-jaxrs</artifactId>   -->
    <!--             <version>${cxf.version}</version>   -->
    <!--         </dependency>   -->
     	<dependency>  
                <groupId>org.apache.cxf</groupId>  
                <artifactId>cxf-rt-frontend-jaxrs</artifactId>  
                <version>${cxf.version}</version>  
            </dependency>
            <dependency>  
                <groupId>javax.ws.rs</groupId>  
                <artifactId>jsr311-api</artifactId>  
                <version>1.1.1</version>  
            </dependency>  
            <dependency>  
                <groupId>org.slf4j</groupId>  
                <artifactId>slf4j-api</artifactId>  
                <version>1.5.8</version>  
            </dependency>  
            <dependency>  
                <groupId>org.slf4j</groupId>  
                <artifactId>slf4j-simple</artifactId>  
                <version>1.5.8</version>  
            </dependency>  
            <dependency>  
                <groupId>commons-httpclient</groupId>  
                <artifactId>commons-httpclient</artifactId>  
                <version>3.0</version>  
            </dependency>  
            <dependency>  
                <groupId>commons-io</groupId>  
                <artifactId>commons-io</artifactId>  
                <version>2.3</version>  
            </dependency>  
            <dependency>  
                <groupId>junit</groupId>  
                <artifactId>junit</artifactId>  
                <version>4.8.1</version>  
                <scope>test</scope>  
            </dependency>  
        </dependencies>  
          
        <build>  
            <finalName>cxfstudy</finalName>  
            <resources>  
                <resource>  
                    <directory>src/main/resources</directory>  
                </resource>  
                <resource>  
                    <directory>src/main/java</directory>  
                    <includes>  
                        <include>**</include>  
                    </includes>  
                    <excludes>  
                        <exclude>**/*.java</exclude>  
                    </excludes>  
                </resource>  
            </resources>  
            <plugins>  
                <plugin>  
                    <groupId>org.mortbay.jetty</groupId>  
                    <artifactId>maven-jetty-plugin</artifactId>  
                    <configuration>  
                        <contextPath>/</contextPath>  
                        <connectors>  
                            <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">  
                                <port>9000</port>  
                            </connector>  
                        </connectors>  
                    </configuration>  
                </plugin>  
                <plugin>  
                    <groupId>org.apache.maven.plugins</groupId>  
                    <artifactId>maven-compiler-plugin</artifactId>  
                    <configuration>  
                        <source>1.7</source>  
                        <target>1.7</target>  
                    </configuration>  
                </plugin>  
            </plugins>  
        </build>  
      
    </project>  
    

      与(1)中介绍的类同,JAXRSServerFactoryBean存在于cxf-rt-frontend-jaxrs中,而集合包cxf-bundle-jaxrs是不必要的

    供资源使用的实体类Room和Person

    package com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement(name="Room")  
    public class Room {  
        public Room()  
        {  
            persons=new HashMap<String,Person>();  
        }  
        String id;  
        Map<String,Person> persons;  
          
        public String getId() {  
            return id;  
        }  
        public void setId(String id) {  
            this.id = id;  
        }  
        public Map<String, Person> getPersons() {  
            return persons;  
        }  
        public void setPersons(Map<String, Person> persons) {  
            this.persons = persons;  
        }  
    } 
    

      注意不要漏掉@XmlRootElement

    package com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model;
    
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement(name="Person")  
    public class Person {  
        private String name;  
        private String sex;  
        public String getName() {  
            return name;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
        public String getSex() {  
            return sex;  
        }  
        public void setSex(String sex) {  
            this.sex = sex;  
        }  
          
    }  
    

      为了解决“No message body writer found for class”,即非简单对象无法序列化的问题,我们暂时先加两个Wrapper类作为Room的集合类和Person集合类,后面会专门介绍如何更优雅的解决这个问题。

    Rooms.java

     1 package com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model;
     2 
     3 import java.util.Map;
     4 
     5 import javax.xml.bind.annotation.XmlRootElement;
     6 
     7 import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.dao.RoomDAO;
     8 
     9 @XmlRootElement(name="rooms")  
    10 public class Rooms {  
    11     Map<String,Room> rooms;  
    12     public Rooms()  
    13     {  
    14         rooms=RoomDAO.getMapOfRooms();  
    15     }  
    16     public Map<String, Room> getRooms() {  
    17         return rooms;  
    18     }  
    19     public void setRooms(Map<String, Room> rooms) {  
    20         this.rooms = rooms;  
    21     }  
    22 }  

    Persons.java

    package com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model;
    
    import java.util.Map;
    
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement(name="persons")  
    public class Persons {  
        Map<String,Person> persons;  
        public Persons()  
        {  
            persons=null;  
        }  
        public Persons(Room room)  
        {  
            persons=room.getPersons();  
        }  
        public Map<String, Person> getPersons() {  
            return persons;  
        }  
        public void setPersons(Map<String, Person> persons) {  
            this.persons = persons;  
        }  
    }  

    实体有了,需要数据,我们虚拟了一个DAO(原本DAO是数据访问层),这里我们直接将所需要测试的数据创建其中

     1 package com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.dao;
     2 
     3 import java.util.HashMap;
     4 import java.util.Map;
     5 
     6 import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Person;
     7 import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Room;
     8 import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Rooms;
     9 
    10 public class RoomDAO {  
    11     private static Map<String, Room> rooms;  
    12     static {  
    13         rooms = new HashMap<String, Room>();  
    14           
    15         Person p1=new Person();  
    16         p1.setName("Boris");  
    17         p1.setSex("male");  
    18           
    19           
    20           
    21         Room r=new Room();  
    22         r.setId("001");  
    23         r.getPersons().put(p1.getName(), p1);  
    24         rooms.put("001", r);  
    25     }  
    26   
    27     public static void addRoom(Room room) {  
    28         rooms.put(room.getId(), room);  
    29     }  
    30   
    31     public static void deleteRoom(String id) {  
    32         if (rooms.containsKey(id)) {  
    33             rooms.remove(id);  
    34         }  
    35   
    36     }  
    37   
    38     public static void updateRoom(String id,Room room) {  
    39         rooms.remove(id);  
    40         rooms.put(room.getId(), room);  
    41     }  
    42   
    43     public static Room getRoom(String id) {  
    44         if (rooms.containsKey(id)) {  
    45             return rooms.get(id);  
    46         } else {  
    47             return null;  
    48         }  
    49     }  
    50     /*operations to persons*/  
    51     public static void addPerson(String id_room,Person person) {  
    52         if(rooms.containsKey(id_room))  
    53         {  
    54             Room room=rooms.get(id_room);  
    55             room.getPersons().put(person.getName(), person);  
    56         }  
    57     }  
    58       
    59     public static Rooms getRooms()  
    60     {  
    61         return new Rooms();  
    62     }  
    63       
    64     public static void deletePerson(String id_room,String name)  
    65     {  
    66         if(rooms.containsKey(id_room))  
    67         {  
    68             Room room=rooms.get(id_room);  
    69             room.getPersons().remove(name);  
    70         }  
    71     }  
    72       
    73     public static Map<String, Room> getMapOfRooms()  
    74     {  
    75         return rooms;  
    76     }  
    77 }  

    到这里,我们基本已经完成准备工作

    那么如何写一个RESTful Web Service呢?

    package com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.service;
    
    import javax.ws.rs.Consumes;
    import javax.ws.rs.DELETE;
    import javax.ws.rs.GET;
    import javax.ws.rs.POST;
    import javax.ws.rs.PUT;
    import javax.ws.rs.Path;
    import javax.ws.rs.PathParam;
    import javax.ws.rs.Produces;
    
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.dao.RoomDAO;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Person;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Persons;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Room;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Rooms;
    
    @Path("/roomservice")  
    @Produces("application/xml")  
    public class RoomService {  
          
        @GET  
        @Path("/room/{id}")  
        @Consumes("application/xml")  
        public Room getRoom(@PathParam("id")String id )  
        {  
            System.out.println("get room by id= "+id);  
            Room room=RoomDAO.getRoom(id);  
            return room;  
        }  
        @GET  
        @Path("/room")  
        @Consumes("application/xml")  
        public Rooms getAllRoom()  
        {  
            System.out.println("get all room");  
            Rooms rooms=RoomDAO.getRooms();  
            return rooms;  
        }  
          
        @POST  
        @Path("/room")  
        @Consumes("application/xml")  
        public void addRoom(Room room)  
        {  
            System.out.println("add room which id is:"+room.getId());  
            RoomDAO.addRoom(room);  
        }  
        @PUT  
        @Path("/room/{id}")  
        @Consumes("application/xml")  
        public void updateRoom(@PathParam("id")String id,Room room)  
        {  
            System.out.println("update room which original id is:"+room.getId());  
            RoomDAO.updateRoom(id,room);  
        }  
        @DELETE  
        @Path("/room/{id}")  
        @Consumes("application/xml")  
        public void deleteRoom(@PathParam("id")String id)  
        {  
            System.out.println("remove room by id= "+id);  
            RoomDAO.deleteRoom(id);  
        }  
        @POST  
        @Path("/room/{id}")  
        @Consumes("application/xml")  
        public void addPerson(@PathParam("id") String id,Person person)  
        {  
            System.out.println("add person who's name is:"+person.getName());  
            RoomDAO.addPerson(id, person);  
        }  
        
        @GET  
        @Path("/room/{id}/person")  
        @Consumes("application/xml")  
        public Persons getAllPersonOfRoom(@PathParam("id") String id)  
        {  
    //         System.out.println("get room by id= "+id);  
    //         Room room=RoomDAO.getRoom(id);  
    //         return room.getPersons();  
    //        TODO No message body writer HashMap
             System.out.println("get room by id= "+id);  
             Room room=RoomDAO.getRoom(id);  
             return new Persons(room);  
        }  
        
        @DELETE  
        @Path("/room/{id}/{name}")  
        @Consumes("application/xml")  
        public void deletePerson(@PathParam("id")String id,@PathParam("name")String name)  
        {  
            System.out.println("remove person who's name is: "+name);  
            RoomDAO.deletePerson(id, name);  
        }  
    }  

    注意在类头上的Annotation @Path和@Produces

    @Path("/roomservice")——指这个服务的根路径,用于区分其他服务

    @Produces("application/xml")——指这个服务生产xml的响应

    最后我们还需一个服务

    我们使用与(1)类似的方法,用JAXRSServerFactoryBean来启动一个服务

    package com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.server;
    
    import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
    
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Person;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Persons;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Room;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Rooms;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.service.RoomService;
    
    public class Server {  
      
        public static void main(String[] args) {  
            RoomService service = new RoomService();  
      
            // Service instance  
            JAXRSServerFactoryBean restServer = new JAXRSServerFactoryBean();  
            restServer.setResourceClasses(Room.class, Person.class, Rooms.class, Persons.class);  
            restServer.setServiceBean(service);  
            restServer.setAddress("http://localhost:9000/rs");  
            restServer.create();  
        }  
    } 

    运行服务Run As... -> Java Application

    124 [main] WARN org.apache.cxf.jaxrs.utils.ResourceUtils - No resource methods have been found for resource class com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Room
    124 [main] WARN org.apache.cxf.jaxrs.utils.ResourceUtils - No resource methods have been found for resource class com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Person
    125 [main] WARN org.apache.cxf.jaxrs.utils.ResourceUtils - No resource methods have been found for resource class com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Rooms
    125 [main] WARN org.apache.cxf.jaxrs.utils.ResourceUtils - No resource methods have been found for resource class com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Persons
    222 [main] INFO org.apache.cxf.endpoint.ServerImpl - Setting the server's publish address to be http://localhost:9000/rs
    284 [main] INFO org.eclipse.jetty.util.log - Logging initialized @500ms
    318 [main] INFO org.eclipse.jetty.server.Server - jetty-9.2.11.v20150529
    330 [main] WARN org.eclipse.jetty.server.handler.AbstractHandler - No Server set for org.apache.cxf.transport.http_jetty.JettyHTTPServerEngine$1@49e202ad
    357 [main] INFO org.eclipse.jetty.server.ServerConnector - Started ServerConnector@7364985f{HTTP/1.1}{localhost:9000}
    357 [main] INFO org.eclipse.jetty.server.Server - Started @582ms
    362 [main] WARN org.eclipse.jetty.server.handler.ContextHandler - Empty contextPath
    369 [main] INFO org.eclipse.jetty.server.handler.ContextHandler - Started o.e.j.s.h.ContextHandler@351d0846{/,null,AVAILABLE}
    

      暂时忽略WARN里面的日志

    然后我们通过浏览器测试

    (测试方式有多种,原则上能对http Interceptor的工具都可以对web service进行测试,eclipse里有自带的工具,TcpTrace、SoapUI也都是比较好用的小工具)

    *扩展,RESTful用json格式

    我们将RoomService.java中所有的"application/xml"替换成"application.json",然后重启服务。

    浏览器访问会出现一行错误提示

    "

    No message body writer has been found for class com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Rooms, ContentType: application/json

    "

    这是因为,CXF默认有xml的序列化provider,我们需要显性指定一个JsonProvider,这里我们先用一个业界比较流行的Jackson来支持。

    先在pom中加入依赖

    <dependency>
      <groupId>com.fasterxml.jackson.jaxrs</groupId>
      <artifactId>jackson-jaxrs-json-provider</artifactId>
      <version>2.6.3</version>
    </dependency>
    

    然后为我们的server实例指定一个JsonProvider: JacksonJsonProvider

    package com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.server;
    
    import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
    
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Person;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Persons;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Room;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Rooms;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.service.RoomService;
    import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
    
    public class Server {  
      
        public static void main(String[] args) {  
            RoomService service = new RoomService();  
      
            // Service instance  
            JAXRSServerFactoryBean restServer = new JAXRSServerFactoryBean();  
            restServer.setResourceClasses(Room.class, Person.class, Rooms.class, Persons.class);  
            restServer.setServiceBean(service);  
            restServer.setAddress("http://localhost:9000/rs");  
            restServer.setProvider(new JacksonJsonProvider()); 
            restServer.create();  
        }  
    } 

    返回浏览器,访问地址:http://localhost:9000/rs/roomservice/room 查看,这时我们看到json格式的数据正常返回了。

     追问:如果我们用cxf自己的JSONProvider会怎样?

    如果用cxf自己的JSONProvider,我们需要引入

    cxf-rt-rs-extension-providers.jar

    而这个jar在运行时还会依赖jettison里面的TypeConverter,所以我们需要引入

    jettison.jar

    <dependency>
        <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-rs-extension-providers</artifactId>
      <version>${cxf.version}</version>
    </dependency>
    		
    <dependency>
      <groupId>org.codehaus.jettison</groupId>
      <artifactId>jettison</artifactId>
      <version>1.3.7</version>
    </dependency>
    

    完事之后将Server里面的Provider换掉

    package com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.server;
    
    import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
    import org.apache.cxf.jaxrs.provider.json.JSONProvider;
    
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Person;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Persons;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Room;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.model.Rooms;
    import com.cnblog.richaaaard.cxftest.standalone.rs.helloworld.service.RoomService;
    
    public class Server {
    
        public static void main(String[] args) {
            RoomService service = new RoomService();
    
            // Service instance
            JAXRSServerFactoryBean restServer = new JAXRSServerFactoryBean();
            restServer.setResourceClasses(Room.class, Person.class, Rooms.class, Persons.class);
            restServer.setServiceBean(service);
            restServer.setAddress("http://localhost:9000/rs");
    //        restServer.setProvider(new JacksonJsonProvider());
            restServer.setProvider(new JSONProvider<Object>());
            restServer.create();
        }
    }

    从浏览器访问,也同样得到了序列化的json:

    不知道有没有人注意两个json string不太一样。这是因为不同库的实现方式不同,Jettison生成的字符串明显≥Jackson的实现。

    那么问题来了

    如何选择json的实现呢?在实际应用中,xml好还是json或是其他格式会更好呢?

      

     

    参考:

    http://www.cnblogs.com/ggjucheng/p/3352477.html

    https://cwiki.apache.org/confluence/display/CXF20DOC/JAXRS+Services+Configuration

  • 相关阅读:
    mysql设置用户密码规则
    jsonp格式前端发送和后台接受写法
    MySql数据库中正则表达式
    linux中服务(service)管理
    第50课 C++对象模型分析(上)
    第49课 多态的概念和意义
    第42课
    第41课
    深入理解MyBatis中的一级缓存与二级缓存
    hibernate一级缓存和二级缓存的区别
  • 原文地址:https://www.cnblogs.com/richaaaard/p/5012343.html
Copyright © 2011-2022 走看看