jersey-stepbystep

创建框架

执行命令,创建一个最基础的应用脚手架

1
2
3
4
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.16

该脚手架使用的是servlet 2.5,主要关注两个地方:

  1. web.xml要声明入口的servlet以及要扫描的资源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <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>
  2. pom.xml使用的依赖包为

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.glassfish.jersey</groupId>
    <artifactId>jersey-bom</artifactId>
    <version>2.17</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet-core</artifactId>
    </dependency>
    </dependencies>

如果使用的是servlet 3.0,则以上两个地方分别为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
web.xml
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- Servlet declaration can be omitted in which case
it would be automatically added by Jersey -->
<servlet>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/webapi/*</url-pattern>
</servlet-mapping>
</web-app>
1
2
3
4
5
pom.xml
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
</dependency>

当然,上述web.xml中的入口也可以配置成Application的子类,并在该子类中声明好各种配置信息,那样在web.xml中就不需要再配置了,对servlet2.5,可以只保留空的web.xml;对于servlet3.0,可以不需要web.xml


添加响应GET的rest服务

增加一个类,声明@Path注解

1
2
3
4
5
@Path("/item")
public class ItemService {
......
}

添加一个方法响应GET请求

1
2
3
4
5
6
7
8
9
@Path("/item")
public class ItemService {
@GET
@Produces(MediaType.APPLICATION_JSON)
public String get() {
return "item1";
}
}

web.xml中添加入口servlet的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
<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.louz.gds</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>

访问http://localhost:8080/simple-service-webapp/webapi/item即可看到页面显示item1

还可以在方法中声明子资源

1
2
3
4
5
6
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public String getById(@PathParam("id") String id) {
return id;
}

访问http://localhost:8080/simple-service-webapp/webapi/item/1即可看到页面显示1


如何接收POST请求

1. 使用JSON-P

首先需要增加对json的支持,先在pom.xml增加如下的依赖

1
2
3
4
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
</dependency>

后台的service方法:

1
2
3
4
5
6
7
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public JsonObject getItem(JsonObject item) { // 使用JsonObject接收请求的json串
System.out.println(item);
return item;
}

2. 使用MOXy

首先需要增加对json的支持,先在pom.xml增加如下的依赖

1
2
3
4
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>

后台的service方法:

1
2
3
4
5
6
7
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Item getItem(Item item) {
System.out.println(item.getId());
return item;
}

注意

无论是JSON-P还是MOXy,对客户端传过来的json串都有比较严格的格式要求,如果不符合的话,会报参数解析出错或者其他奇奇怪怪的错,正确的json串格式应该是:
{"属性名1": 值1, "属性名2": 值2}属性名两边的双引号一定要有!可以利用JSON.stringify(jsonObj)方法对json对象进行字符串化,以下是使用jQuery的前端代码样例:

1
2
3
4
5
6
7
8
9
10
11
$.ajax({
url: "webapi/item",
type: "POST",
// data: JSON.stringify({id: 1}), 效果与下一行的一样
data: '{"id": 1}',
success: function (data) {
alert(data.id);
},
contentType: "application/json",
dataType: "json"
});


如何做单元测试

下面例子使用JSON-P进行描述。首先在pom.xml增加jersey的单元测试框架依赖:

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<scope>test</scope>
</dependency>

编写测试类(继承JerseyTest类)即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ItemServiceTest extends JerseyTest {
@Override
protected Application configure() {
final ResourceConfig config = new ResourceConfig(GridStrategyResource.class);
// 为便于调试,一般启用以下两个选项
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
return config;
}
@Test
public void testPostItem() {
JsonObject doc = Json.createObjectBuilder()
.add("id", "1")
.build();
final Response response = target("item").request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(doc));
System.out.println(response);
assertEquals(200, response.getStatus());
}
}

与spring集成

首先在pom.xml引入相关依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<dependencyManagement>
<dependencies>
...
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>3.2.13.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
...
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>

web.xml声明spring的配置文件位置

1
2
3
4
5
6
7
8
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

增加spring的配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.louz.gds"/>
</beans>

Spring的bean可以在上面的配置文件中声明,也可以通过@Component/@Repository/@Service/@Controller的方式声明。

Jersey的资源类注入Spring的bean可以通过以下方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Path("/item")
public class ItemService {
// @Resource(name = "itemDao") 如果该类没有声明成@Component,@Resource无法生效
@Autowired
@Qualifier("itemDao") // @Autowired默认按类注入,使用@Qualifier可以按bean名称注入
// @Inject
// @Named("itemDao") // 与使用@Autowired+@Qualifier等效
private ItemDao myItemDao;
...
}
@Repository(value = "itemDao")
public class ItemDao {
public void saveItem(JsonObject item) {
System.out.println("save in dao");
}
}

注意,当Jersey与spring集成测试时,Jersey容器默认加载classpath:applicationContext.xml文件作为spring的配置文件,如果要加载指定文件,需要做以下处理:

1
2
3
4
5
6
7
8
9
10
public class ResourceTest extends JerseyTest {
@Override
protected Application configure() {
final ResourceConfig config = new ResourceConfig(Resource.class);
config.property("contextConfigLocation", "classpath:my.spring.xml"); // 该参数名和值与web.xml里配置的一致
return config;
}
......