引言
在Java编程中,泛型为我们提供了一种类型安全的方法来使用集合。然而,在运行时,Java的类型擦除机制会导致泛型信息丢失,这在JAX-RS框架中尤为明显。本文将通过一个具体的例子,探讨如何使用GenericEntity
来解决这一问题。
问题背景
在JAX-RS中,当我们尝试将一个泛型对象(如List<Order>
)作为响应体返回时,由于类型擦除,它在运行时将表现为一个原始的List<?>
。这会导致某些MessageBodyWriter
实现无法正确选择,因为它们需要泛型信息来序列化对象。
直接使用Response的示例
首先,我们来看一个直接将实体对象包装在Response
中的示例:
@Path("/")
public class OrderResource {
@Path("/orders")
@Produces(MediaType.APPLICATION_XML)
public Response handle() {
List<Order> orders = getOrders();
Response r = Response.ok(orders)
.header("someHeader", "someHeaderValue")
.build();
return r;
}
public List<Order> getOrders() {
Order o1 = new Order(1, new BigDecimal("15"));
Order o2 = new Order(2, new BigDecimal("20"));
return Arrays.asList(o1, o2);
}
// ...省略其他部分
}
public class Order {
private int id;
private BigDecimal amount;
// ...省略其他部分
}
遇到的问题
当我们尝试运行上述代码时,会遇到一个500内部服务器错误的异常:
Caused by: javax.ws.rs.InternalServerErrorException: HTTP 500 Internal Server Error
服务器控制台输出:
SEVERE: MessageBodyWriter not found for media type=application/xml, type=class java.util.Arrays$ArrayList, genericType=class java.util.Arrays$ArrayList.
这表明由于泛型信息的丢失,JAX-RS无法找到合适的MessageBodyWriter
来序列化List<Order>
。
使用GenericEntity解决问题
为了解决这个问题,我们可以使用GenericEntity
来包装我们的实体对象,从而保留泛型信息:
@Path("/")
public class OrderResource {
// ...省略其他部分
@Path("/orders2")
@Produces(MediaType.APPLICATION_XML)
@GET
public Response handle2() {
List<Order> orders = getOrders();
GenericEntity<List<Order>> genericEntity = new GenericEntity<List<Order>>(orders) {};
Response r = Response.ok(genericEntity)
.header("someHeader", "someHeaderValue")
.build();
return r;
}
}
测试结果
使用GenericEntity
后,客户端能够正确接收到序列化后的XML响应:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><orders><order><amount>15</amount><id>1</id></order><order><amount>20</amount><id>2</id></order></orders>
示例项目
本示例项目使用了以下依赖和技术:
jersey-container-servlet
2.25.1:Jersey核心Servlet 3.x实现。- JDK 1.8
- Maven 3.3.9
结语
通过使用GenericEntity
,我们可以有效地解决JAX-RS中由于泛型信息丢失导致的问题。这不仅提高了代码的可读性和可维护性,也确保了API的正确性和稳定性。希望本文能够帮助开发者在面对类似问题时,能够快速找到解决方案。