RESTFul 快速实现

一.第一个Java RESTFul 服务

本节讲述基于JavaSE环境的Jersey 官文文档中提供的示例simple-service,并在此基础上扩展自定义的RESTFul 资源服务

1.1 环境准备

准备开发RESTFul 服务的环境 包括JDK Maven 和 IDEA

1.2 创建服务

1.从Maven 原型创建项目

Jersey 官方文档中的提供的例子simple-service 是一个Maven 原型项目,在控制台执行如下命令来生成我们想要的simole-service 项目,项目的存储路径可以自行选择

mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 -DarchetypeGroupId=org.glassfish.jersey.archetypes 
-DinteractiveMode=false -DgroupId=com.example -DartifactId=simple-service -Dpackage=com.example -DarchetypeVersion=2.26

控制台命令成功执行之后,会在当前目录下创建simple-service目录。该目录包含了simple-service 项目的源代码

RESTFul 快速实现_第1张图片
simple-service

2.项目入口类分析
因为这是一个javaSE项目 所以需要一个入口类来启动服务并加载项目资源

package com.example;

import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.jdkhttp.JdkHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.simple.SimpleContainer;
import org.glassfish.jersey.simple.SimpleContainerFactory;
import java.io.IOException;
import java.net.URI;

/**
 * Main class.
 *
 */
public class Main {
    // 服务器路径
    public static final String BASE_URI = "http://localhost:8080/myapp/";
    public static HttpServer startServer() {
        // 加载资源
        final ResourceConfig rc = new ResourceConfig().packages("com.example");

        //创建和启动grizzly http 服务器
        return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
    }
    public static com.sun.net.httpserver.HttpServer startServerByHTTPServer(){
        final ResourceConfig rc = new ResourceConfig().packages("com.example");
        return JdkHttpServerFactory.createHttpServer(URI.create(BASE_URI),rc);
    }
    /**
     * 启动服务器
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        final HttpServer server = startServer();
        System.out.println(String.format("Jersey app started with WADL available at "
                + "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
        System.in.read();
        server.stop();
    }
}

Main类定义了HTTP服务器的路径即http://localhost:8080/myapp/ .在其构造器中映射了源代码所在的包名为 new ResourceConfig().packages("com.example"); 这意味着 服务器启动时会自动扫描该包下的所有类,根据该包中所含类的REST资源路径的注解,在内存中做好映射。这样一来客户端请求指定路径后服务端就可以根据映射,分派请求给相应的资源类实例的相应的方法了

2.资源类分析
套用Web 开发环境中典型的三层逻辑,资源类位于逻辑分层的最高层----API层 其下为Service 层和数据访问层,在三层逻辑中,API层用于对外公布接口

package com.example;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

/**
 * Root resource (exposed at "myresource" path)
 */
@Path("myresource")
public class MyResource {

    /**
     * Method handling HTTP GET requests. The returned object will be sent
     * to the client as "text/plain" media type.
     *
     * @return String that will be returned as a text/plain response.
     */
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String getIt() {
        return "Got it!";
    }
}

1.3 扩展服务

1.增加设备实体类(在com.domain 包下新建一个Device 类)

package com.domain;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="device")
public class Device {

    private String deviceIP;
    private int deviceStatus;

    public Device(){

    }

    public Device(String deviceIP){
        this.deviceIP = deviceIP;
    }

    public String getDeviceIP() {
        return deviceIP;
    }

    public void setDeviceIP(String deviceIP) {
        this.deviceIP = deviceIP;
    }

    @XmlAttribute
    public int getDeviceStatus() {
        return deviceStatus;
    }

    public void setDeviceStatus(int deviceStatus) {
        this.deviceStatus = deviceStatus;
    }
}
package com.dao;

import com.domain.Device;
import com.sun.org.apache.bcel.internal.generic.RETURN;

import java.util.concurrent.ConcurrentHashMap;

public class DeviceDao {

    private ConcurrentHashMap fakeDB = new ConcurrentHashMap<>();

    public DeviceDao(){
        fakeDB.put("127.0.0.1",new Device("127.0.0.1"));
        fakeDB.put("192.168.4.74",new Device("192.168.4.74"));
    }

    public Device getDevice(String IP){
        return fakeDB.get(IP);
    }

    public Device updateDevice(Device device){
        String IP = device.getDeviceIP();
        fakeDB.put(IP,device);
        return fakeDB.get(IP);
    }
}

该类标注了JAXB 标准定义的@XmlRootElement 和 @XmlAttribute 注解 以便将Device类和XML格式的设备数据相互转化并在服务器和客户端之间传输

2.增加设备资源类
创建了设备实体类之后,我们需要一个资源来公布设备的REST API

package com.example;

import com.dao.DeviceDao;
import com.domain.Device;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import java.util.List;
import java.util.Map;

@Path(value = "device")
public class DeviceResource {

    private DeviceDao deviceDao;

    public DeviceResource(){
        deviceDao = new DeviceDao();
    }

    @GET
    @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
    public Device get(@QueryParam("ip") final String deviceIP){
        Device device = null;
        if (deviceIP != null){
            device = deviceDao.getDevice(deviceIP);
        }
        return device;
    }

    @PUT
    @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
    public Device put(final Device device){
        Device result = null;
        if (device != null){
            result = deviceDao.updateDevice(device);
        }
        return result;
    }

}

@PUT是标注处理put请求 @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML}) 是标注返回实体的类型,支持JSON 和XML 数据格式

3.测试和运行服务

package com.example;

import com.domain.Device;
import org.glassfish.grizzly.http.server.HttpServer;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import java.util.HashMap;
import java.util.Map;

public class DeviceResourceTest {
    private HttpServer httpServer;
    private WebTarget target;
    @Before
    public void setUp() throws Exception{
        httpServer = Main.startServer();
        final Client client = ClientBuilder.newClient();
        target = client.target(Main.BASE_URI);
    }
    @After
    public void tearDown() throws Exception{
        httpServer.shutdown();
    }
    @Test
    public void testGetDevice(){
        final String targetIP = "127.0.0.1";
        final Device device = target.path("device").queryParam("ip",targetIP).request().get(Device.class);
        Assert.assertEquals(targetIP,device.getDeviceIP());
    }
    @Test
    public void testPutDevice(){
        Device device = new Device();
        device.setDeviceIP("192.168.5.5");
        device.setDeviceStatus(2);
        Entity entity = Entity.entity(device, MediaType.APPLICATION_XML_TYPE);
        Device result = target.path("device").request().put(entity,Device.class);
        Assert.assertEquals("192.168.5.5",result.getDeviceIP());
    }

    @Test
    public void testPost(){
        Device device = new Device();
        device.setDeviceIP("192.168.5.5");
        device.setDeviceStatus(2);
        Entity entity = Entity.entity(device, MediaType.TEXT_PLAIN_TYPE);
        String result = target.path("device").request().post(entity,String.class);
        Assert.assertEquals(result,"SUCCESS");
    }
}

打开控制台 在项目的目录下运行 :mvn clean test 如果测试通过 即断言验证成功

二.第一个Servlet 容器 服务

1.1 创建和分析Web 服务

simple-service-webapp项目也是Jersey官方文档中的例子,同样是个Maven原型

mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-webapp 
                -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false 
                -DgroupId=com.example -DartifactId=simple-service-webapp -Dpackage=com.example
                -DarchetypeVersion=2.26

在 控制台下 执行该命令 就可以获取源代码了

RESTFul 快速实现_第2张图片
simple-service-webapp

通过maven 将项目打包 部署至tomcat运行即可

三.REST 服务类型

1.REST 服务分为四种类型

RESTFul 快速实现_第3张图片
四种类型
  • 类型一:当服务中没有Application 子类时 容器会查找Servlet的子类来做入口,如果Servlet 的子类也不存在,则REST服务类型为类型一
  • 类型二:当服务类中没有Application 子类,但存在Servlet的子类时,则REST 服务类型为类型二
  • 类型三:服务中定义了Application 的子类 而且这个Application 的子类使用了@ApplicationPath注解 则REST服务类型为类型三
  • 类型四:如果服务中定义了Application 的子类 但是这个Application 的子类没有使用@ApplicationPath注解 则REST服务类型为类型四

2.REST 服务类型一

类型一相应的逻辑是服务中同时不存在Application的子类和Servlet的子类,因此需要为REST服务动态生成一个名为javax.ws.rs.core.Application 的Servlet 实例,并自动探测匹配资源,需要在 web.xml 下配置




    
        Jersey Web Application
        org.glassfish.jersey.servlet.ServletContainer
        
            jersey.config.server.provider.packages
            com.example
        
        1
    
    
        Jersey Web Application
        /webapi/*
    


3.REST 服务类型二
类型二 相应的逻辑不存在Application 的子类 ,但存在Servlet的子类,因此需要有个类继承自HttpServlet

package com.example;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

import org.glassfish.jersey.servlet.ServletContainer;

/**
 * 类型二:不存在Application子类,存在Servlet的子类,ServletContainer 继承自HttpServlet
 */
@WebServlet(initParams = @WebInitParam(name = "jersey.config.server.provider.packages", value = "com.example"),
urlPatterns = "/webapi/*", 
loadOnStartup = 1)
public class AirServlet extends ServletContainer {
    private static final long serialVersionUID = 1L;
}

四.REST 服务类型三
类型三 相应的逻辑存在Application 的子类并且定义了@ApplicationPath 注解

package com.example;

import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;

@ApplicationPath("/webapi/*")
public class AirResourceConfig extends ResourceConfig {
    public AirResourceConfig() {
        packages("com.example");
    }
}

五.REST 服务类型四
类型四 不存在Servlet 子类 也不存在或者不允许使用@ApplicationPath 注解

package com.example;

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.Application;

public class AirApplication extends Application {
    @Override
    public Set> getClasses() {
        final Set> classes = new HashSet>();
        classes.add(MyResource.class);
        return classes;
    }
}

你可能感兴趣的:(RESTFul 快速实现)