一.第一個Java RESTFul 服務(wù)
本節(jié)講述基于JavaSE環(huán)境的Jersey 官文文檔中提供的示例simple-service,并在此基礎(chǔ)上擴展自定義的RESTFul 資源服務(wù)
1.1 環(huán)境準(zhǔn)備
準(zhǔn)備開發(fā)RESTFul 服務(wù)的環(huán)境 包括JDK Maven 和 IDEA
1.2 創(chuàng)建服務(wù)
1.從Maven 原型創(chuàng)建項目
Jersey 官方文檔中的提供的例子simple-service 是一個Maven 原型項目,在控制臺執(zhí)行如下命令來生成我們想要的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
控制臺命令成功執(zhí)行之后,會在當(dāng)前目錄下創(chuàng)建simple-service目錄。該目錄包含了simple-service 項目的源代碼

2.項目入口類分析
因為這是一個javaSE項目 所以需要一個入口類來啟動服務(wù)并加載項目資源
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 {
// 服務(wù)器路徑
public static final String BASE_URI = "http://localhost:8080/myapp/";
public static HttpServer startServer() {
// 加載資源
final ResourceConfig rc = new ResourceConfig().packages("com.example");
//創(chuàng)建和啟動grizzly http 服務(wù)器
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);
}
/**
* 啟動服務(wù)器
* @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服務(wù)器的路徑即http://localhost:8080/myapp/ .在其構(gòu)造器中映射了源代碼所在的包名為 new ResourceConfig().packages("com.example"); 這意味著 服務(wù)器啟動時會自動掃描該包下的所有類,根據(jù)該包中所含類的REST資源路徑的注解,在內(nèi)存中做好映射。這樣一來客戶端請求指定路徑后服務(wù)端就可以根據(jù)映射,分派請求給相應(yīng)的資源類實例的相應(yīng)的方法了
2.資源類分析
套用Web 開發(fā)環(huán)境中典型的三層邏輯,資源類位于邏輯分層的最高層----API層 其下為Service 層和數(shù)據(jù)訪問層,在三層邏輯中,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 擴展服務(wù)
1.增加設(shè)備實體類(在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<String,Device> 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);
}
}
該類標(biāo)注了JAXB 標(biāo)準(zhǔn)定義的@XmlRootElement 和 @XmlAttribute 注解 以便將Device類和XML格式的設(shè)備數(shù)據(jù)相互轉(zhuǎn)化并在服務(wù)器和客戶端之間傳輸
2.增加設(shè)備資源類
創(chuàng)建了設(shè)備實體類之后,我們需要一個資源來公布設(shè)備的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是標(biāo)注處理put請求 @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML}) 是標(biāo)注返回實體的類型,支持JSON 和XML 數(shù)據(jù)格式
3.測試和運行服務(wù)
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<Device> 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<Device> 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 容器 服務(wù)
1.1 創(chuàng)建和分析Web 服務(wù)
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
在 控制臺下 執(zhí)行該命令 就可以獲取源代碼了

通過maven 將項目打包 部署至tomcat運行即可
三.REST 服務(wù)類型
1.REST 服務(wù)分為四種類型

- 類型一:當(dāng)服務(wù)中沒有Application 子類時 容器會查找Servlet的子類來做入口,如果Servlet 的子類也不存在,則REST服務(wù)類型為類型一
- 類型二:當(dāng)服務(wù)類中沒有Application 子類,但存在Servlet的子類時,則REST 服務(wù)類型為類型二
- 類型三:服務(wù)中定義了Application 的子類 而且這個Application 的子類使用了@ApplicationPath注解 則REST服務(wù)類型為類型三
- 類型四:如果服務(wù)中定義了Application 的子類 但是這個Application 的子類沒有使用@ApplicationPath注解 則REST服務(wù)類型為類型四
2.REST 服務(wù)類型一
類型一相應(yīng)的邏輯是服務(wù)中同時不存在Application的子類和Servlet的子類,因此需要為REST服務(wù)動態(tài)生成一個名為javax.ws.rs.core.Application 的Servlet 實例,并自動探測匹配資源,需要在 web.xml 下配置
<?xml version="1.0" encoding="UTF-8"?>
<!-- This web.xml file is not required when using Servlet 3.0 container,
see implementation details http://jersey.java.net/nonav/documentation/latest/jax-rs.html -->
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.example</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/webapi/*</url-pattern>
</servlet-mapping>
</web-app>
3.REST 服務(wù)類型二
類型二 相應(yīng)的邏輯不存在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 服務(wù)類型三
類型三 相應(yīng)的邏輯存在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 服務(wù)類型四
類型四 不存在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<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(MyResource.class);
return classes;
}
}