【转载】在Jersey JAX-RS 处理泛型List等Collection
在Java中,从1.5开始,我们就可以使用泛型了(generic),这看上去很像C++ Template,但是实际上它们是不同的。在这里我不想过多的描述细节,你可以从Google上搜索一下。 但是,泛型已经变得如此复杂,以至于已经有500多页的 FAQ。
我们长话短说:泛型提供了编译时类型安全,所以也消除了类型转换的(cast)的需要。它是通过被称为类型消除(type erasure)的编译时技术来实现的。 泛型FAQ解释了所有的细节,对我来说它就是Java泛型的圣经。
在有些情况下,我们需要从JAXRS的Response类的资源方法中饭后参数化的类型。 因为类型消除,使得此种情况下Jersey运行环境需要特殊的处理来决定为泛型类型选择相应的MessageBodyWriter。
对于使用Jersey的JAX-RS的开发者而言有很多可供选择的技术,下面我将详细的讨论其中的几个。
让我们看一个简单的域模型:Employee。
package net.javasight; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "employee")
public class EmployeeBean {
private Long id;
private String firstName;
private String lastName; public EmployeeBean() {
// required for JAXB
} public EmployeeBean(Long id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
} public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getFirstName() {
return firstName;
} public void setFirstName(String firstName) {
this.firstName = firstName;
} public String getLastName() {
return lastName;
} public void setLastName(String lastName) {
this.lastName = lastName;
}
}
employee资源的一个实现如下:
@Path("/employees")
public class EmployeeResource {
@GET
public Collection<EmployeeBean> getEmployees() {
EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
return Collections.singletonList(emp);
}
}
在这种情况下,我们从资源方法中返回了EmployeeBean的Collection集合。如果你从如下地址(http://localhost:9998/employees)访问该资源,将会产生如下的XML输出:
<? xml version = "1.0" encoding = "UTF-8" standalone = "yes" ?>
<employeeBeans>
<employee>
<firstName>John</firstName>
<id>1</id>
<lastName>Doe</lastName>
</employee>
</employeeBeans>
我希望看到employee在<employees>标签中,而不是在<employeeBeans>中。这里需要做一些额外的配置来产生该格式。那么让我们修改下Employee的POJO来包含该集合。
package net.javasight; import java.util.List; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement
public class Employees {
private List<EmployeeBean> employee; public Employees(List<EmployeeBean> employee) {
this.employee = employee;
} public Employees() {
// required for JAXB
} public List<EmployeeBean> getEmployee() {
return employee;
} public void setEmployee(List<EmployeeBean> employee) {
this.employee = employee;
}
}
让我们完成EmployeeResource的方法来使用自定义的POJO来产生相关的XML。
package net.javasight;
@Path("/employees")
public class EmployeeResource {
@GET
@Path("test1")
public Employees getEmployees1() {
EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
Employees employees = new Employees(Collections.singletonList(emp));
return employees;
}
@GET
@Path("test2")
public Response getEmployees2() {
EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
Employees employees = new Employees(Collections.singletonList(emp));
return Response.ok(employees).build();
}
}
现在访问http://localhost:9988/employees/test1或http://localhost:9998/employees/test2应该会产生如下的XML:
<? xml version = "1.0" encoding = "UTF-8" standalone = "yes" ?>
<employees>
<employee>
<firstName>John</firstName>
<id>1</id>
<lastName>Doe</lastName>
</employee>
</employees>
但是,难道我们需要这样糟糕的方法来产生该输出吗?已经不需要了,在Jersey1.2发布后有了一些改善。可以启用资源配置的FEATURE_XMLROOTELEMENT_PROCESSING 特性后,应当可以自动的产生该输出。因此,访问http://localhost:9998/employees/test1应该产生这种的格式XML。该属性默认是禁用的。
现在让我们看看在JAX-RS Response中返回参数化类型会遇到的实际的问题。我在EmployeeResource中添加了另一个方法。
@GET
@Path("test3")
@Produces(MediaType.APPLICATION_XML)
public Response getEmployees3() {
EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
List<EmployeeBean> list = new ArrayList<EmployeeBean>();
list.add(emp);
return Response.ok(list).build();
}
现在通过http://localhost:9998/employees/test3访问该方法应该会产生如下异常。我相信该异常在Jersey/JAX-RS用户中应该很常见。
SEVERE: A message body writer for Java class java.util.ArrayList, and Java type class java.util.ArrayList, and MIME media type application/xml was not found
Jul 24 , 2010 11 : 58 : 55 PM com.sun.jersey.spi.container.ContainerResponse write
SEVERE: The registered message body writers compatible with the MIME media type are:
application/xml ->
com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$App
com.sun.jersey.core.impl.provider.entity.DocumentProvider
com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$App
com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$App
*/* ->
com.sun.jersey.core.impl.provider.entity.FormProvider
com.sun.jersey.server.impl.template.ViewableMessageBodyWriter
com.sun.jersey.core.impl.provider.entity.StringProvider
com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
com.sun.jersey.core.impl.provider.entity.FileProvider
com.sun.jersey.core.impl.provider.entity.InputStreamProvider
com.sun.jersey.core.impl.provider.entity.DataSourceProvider
com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
com.sun.jersey.core.impl.provider.entity.ReaderProvider
com.sun.jersey.core.impl.provider.entity.DocumentProvider
com.sun.jersey.core.impl.provider.entity.StreamingOutputProvider
com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
Jul 24 , 2010 11 : 58 : 55 PM com.sun.jersey.spi.container.ContainerResponse traceException
SEVERE: Mapped exception to response: 500 (Internal Server Error)
javax.ws.rs.WebApplicationException
要修复该问题,我们需要告诉JAX-RS运行时响应实体的类型,在我们的情况下是Employee的集合。JAX-RS APIGenericEntity正是用于该目的。GenericEntity可以用于反映响应实体中的泛型类型。 EmployeeResource方法被更新在返回集合类型时使用GenericEntity。
@GET
@Path("test4")
@Produces(MediaType.APPLICATION_XML)
public Response getEmployees4() {
EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
List<EmployeeBean> list = new ArrayList<EmployeeBean>();
list.add(emp);
GenericEntity entity = new GenericEntity<List<EmployeeBean>>(list) {
};
return Response.ok(entity).build();
}
访问http://localhost:9998/employees/test4应该会产生我们所需要的输出。
除了这种方式,Jersey1.2引入了新的APIJResponse来支持这个,并且更优。JResponse是类型安全的Response,它保留了响应实体的类型信息,因此不需要额外的使用GenericEntity。
使用JResponse更新后的方法如下:
@GET
@Path("test5")
@Produces(MediaType.APPLICATION_XML)
public JResponse<List<EmployeeBean>> getEmployees5() {
EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
List<EmployeeBean> list = new ArrayList<EmployeeBean>();
list.add(emp);
return JResponse.ok(list).build();
}
访问http://localhost:9998/employees/test5应该会产生我们所需要的输出.
这两种方式实现起来都很容易。主要的不同是GenericEntity是JAX-RS API然而JResponse是Jersey API,它可能不能在其他的JAX-RS实现中使用,因此不具备移植性。 如果你仅仅使用Jersey,那么JResponse是更好的选择,因为它是类型安全的并与Response兼容。
下面是完整的服务端代码:
package net.javasight; import com.sun.grizzly.http.SelectorThread;
import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;
import com.sun.jersey.core.util.FeaturesAndProperties;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map; public class Main {
private static int getPort(int defaultPort) {
String port = System.getenv("JERSEY_HTTP_PORT");
if (null != port) {
try {
return Integer.parseInt(port);
} catch (NumberFormatException e) {
}
}
return defaultPort;
} private static URI getBaseURI() {
return UriBuilder.fromUri("http://localhost/").port(getPort(9998))
.build();
} public static final URI BASE_URI = getBaseURI(); protected static SelectorThread startServer() throws IOException {
final Map<String, String> initParams = new HashMap<String, String>();
initParams.put("com.sun.jersey.config.property.packages",
"com.employee.resources");
initParams.put(FeaturesAndProperties.FEATURE_XMLROOTELEMENT_PROCESSING,
"true");
System.out.println("Starting grizzly...");
SelectorThread threadSelector = GrizzlyWebContainerFactory.create(
BASE_URI, initParams);
return threadSelector;
} public static void main(String[] args) throws IOException {
SelectorThread threadSelector = startServer();
System.out
.println(String
.format(
"Jersey app started with WADL available at "
+ "%sapplication.wadl\nTry out %shelloworld\nHit enter to stop it...",
BASE_URI, BASE_URI));
System.in.read();
threadSelector.stopEndpoint();
}
}
以上内容转自http://javasight.net/2011/05/generified-collections-in-jersey/,感谢作者,但是作者只提供了服务端的泛型处理,并没有提供客户端如何使用泛型。
个人解决客户端接受泛型方法:
服务端代码:
@GET
@Path("test4")
@Produces(MediaType.APPLICATION_JSON)
public Response getEmployees4() {
EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
EmployeeBean emp1 = new EmployeeBean(2L, "zhangsan", "lisi");
List<EmployeeBean> list = new ArrayList<EmployeeBean>();
list.add(emp);
list.add(emp1);
GenericEntity<List<EmployeeBean>> entity = new GenericEntity<List<EmployeeBean>>(list) {
};
return Response.ok(entity).build();
}
客户端代码:
GenericType<List<EmployeeBean>> gt = new GenericType<List<EmployeeBean>>(){};
Object obj = wr.path("/message/test4").get(gt);
System.out.println(obj);
加红字体是rest服务地址 wr是webResource对象。
【转载】在Jersey JAX-RS 处理泛型List等Collection的更多相关文章
- 【转载】 C#中常见的泛型集合类有哪些
在C#语言编程过程中,List集合类是最常见的泛型集合类,其实除了List集合,还有其他一些常用的泛型集合类,如字典类型Dictionary泛型集合类.先进先出的队列类型Queue泛型集合类.后进先出 ...
- 转载.NET 4.0中的泛型的协变和逆变
先做点准备工作,定义两个类:Animal类和其子类Dog类,一个泛型接口IMyInterface<T>, 他们的定义如下: public class Animal { } public ...
- 转载:C#中的泛型
泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确定一个或多个具 ...
- C# 泛型集合
原文出处我的wiki,转载请说明出处 考虑到泛型在集合类型中的广泛应用,这里一起讨论. 1. 泛型 1.1 泛型的定义与约束 创建泛型方法.委托.接口或类时,需要在名称后增加尖括号及其中的泛型参数,泛 ...
- 【转载】C#中List集合使用AddRange方法将一个集合加入到指定集合末尾
C#编程开发过程中,List集合是时常使用到的集合对象,如果在List集合的操作中需要将1个List集合加入到另一个List集合的末尾,则可以使用List集合的AddRange方法来实现,AddRan ...
- Java泛型
什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个 ...
- C#中的泛型 【转】
C#中的泛型 泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性.泛型为.NET框架引入了类型参数(type parameters)的概念.类型参数使得设计类和方法时,不必确 ...
- Java泛型总结
1. 什么是泛型?泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的 ...
- java泛型的讲解
java泛型 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指 ...
随机推荐
- iOS开发值NSUserDefaults类概述
NSUserDefaults类概述 NSUserDefaults类为和默认的系统进行交互提供了一个系统编程接口.默认的系统允许一个应用来定制它的行为以适应用户的喜好.例如,你可以允许用户去决定你的应用 ...
- 如何利用gdb调试程序?
程序的大体意思就是各一个数组赋值,然后输出来程序已经写在了上面,下面我们就来看一下如何产生带有调试信息的编译文件,这里我们要使用gcc的-g参数,用于在编译文件中加入一些调试信息.发现加了-g和没有加 ...
- Codeforces Round #429 (Div. 2) E. On the Bench
E. On the Bench time limit per test 2 seconds memory limit per test 256 megabytes input standard inp ...
- HBase学习笔记——配置及Shell操作
1.HBase的配置 还是以前配置的集群,见:http://www.cnblogs.com/DarrenChan/p/6493373.html 我们约定:weekend03和weekend04放HMa ...
- 【iOS与EV3混合机器人编程系列之四】iOS_WiFi_EV3_Library 剖析之中的一个:WiFi UDP和TCP
在上一篇文章中.我们通过编写EV3 Port Viewer项目实现了iOS监測EV3的实时端口数据. 程序最核心的部分就是我们的开源码库iOS_WiFi_EV3_Library. 那么,在本文中,我们 ...
- 【实验二】Spring框架笔记——NamedParameterJdbcTemplate与具名参数
在经典的 JDBC 用法中, SQL 参数是用占位符 ? 表示,并且受到位置的限制. 定位参数的问题在于, 一旦参数的顺序发生变化, 就必须改变参数绑定. 在 Spring JDBC 框架中, 绑定 ...
- C0301 代码块{}的使用,重定向, 从文件中读取行
#!/bin/bash # 从 /etc/fstab 中读行 File=/etc/fstab { read line1 read line2 } < $File # {}代码块, ...
- Swift-8-枚举
// Playground - noun: a place where people can play import UIKit // 枚举语法 enum SomeEnumeration { // e ...
- ACM 博弈(难)题练习 (第一弹)
第二弹: 套路&&经验总结: 1. N堆***的游戏,一般可以打表找SG函数的规律.比如CodeForces 603C 2.看起来是单轮的游戏,实际上可能拆分成一些独立的子游戏.比如C ...
- 《C++程序设计》朝花夕拾
(以后再也不用破Markdown写东西了,直到它有一个统一的标准,不然太乱了--) 函数签名 int f (int a, int b) ↑ ↑ ↑ ↑ 返回类型 函数名 形 式 参 数 其中,函数 ...