通过 GenericEntity<List<T>> 在 RESTful Response 对象中使用 Java 泛型模板类型

2022-09-03 13:04:02

我有一个泛型 JAX-RS 资源类,并且定义了一个泛型方法findAll

public abstract class GenericDataResource<T extends GenericModel> {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response findAll() {
        Query query = em.createNamedQuery(modelClass.getSimpleName()+".findAll");
        List<T> list = query.getResultList();
        return Response.ok(new GenericEntity<List<T>>(list) {}).build();
    }
}

和用户类:

public class User extends GenericModel {
    ...
}

下面是示例子类定义:

@Path("users")
public class UserResource extends GenericDataResource<User> {

    public UserResource() {
        super(User.class);
    }
}

我得到以下例外:

com.sun.jersey.api.MessageException: A message body writer for Java class 
java.util.Vector, and Java type java.util.List<T>, 
and MIME media type application/json was not found exception.

如果我用一个定义的类替换T,如下所示:User

GenericEntity<List<User>>(list)

然后它工作正常。

关于我如何使它与通用T一起工作的任何想法?


答案 1

编译源代码后,由以下行创建的(匿名)类:

new GenericEntity<List<T>>(list) {}

使用类型变量来引用其父级。由于类型变量在运行时没有值,因此不能使用这样的泛型。您被迫从调用站点传递所谓的类型令牌。这是一个示例,它要求从 的调用方传递令牌,但您可以在构造函数中要求一个令牌,并将其保存在实例变量中:findAll()

public abstract class GenericDataResource<T extends GenericModel> {
  public Response findAll(GenericEntity<List<T>> token) {
    Query query = em.createNamedQuery(modelClass.getSimpleName() + ".findAll");
    List<T> list = query.getResultList();
    return Response.ok(token).build();
  }
}

调用方将发送一个令牌,如

new GenericEntity<List<User>>() {}

如果您只使用非参数化子类,则可以利用反射来构建令牌(未经测试,希望您明白):findAll()

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response findAll() {
  Query query = em.createNamedQuery(modelClass.getSimpleName()+".findAll");
  List<T> list = query.getResultList();
  return Response.ok(new GenericEntity(list, getType())).build();
}

您必须实现才能返回所需的类型。它将是能够表示该类型的参数化类型的子类getType()List<DAO<User>>


答案 2

除了答案之外,要在客户端上读回 Response 对象,请执行以下操作:

List<MyObject> list = response.readEntity(new GenericType<List<MyObject>>(){}));