【问题标题】:How does MessageBodyReader know what the generic type is?MessageBodyReader 如何知道泛型类型是什么?
【发布时间】:2016-05-14 06:28:36
【问题描述】:

我有一个 MessageBodyReader 有以下课程:

@Provider
@Consumes(MediaType.APPLICATION_JSON)
public class TransactionMessageBodyReader implements MessageBodyReader<Transaction<Customer>> 

boolean isReadable(Class<?> type,
                       Type genericType,
                       Annotation[] annotations,
                       MediaType mediaType)

我有以下 REST 端点:

@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("trans")
public Response checkStatus(Transaction<Customer> transaction) { ... }

在我的调试器中,我在isReadable 上有一个断点,它在调用checkStatus 方法之前到达断点。目前很好。我可以在我的调试器中看到genericTypeTransaction&lt;Customer&gt; ...换句话说,它知道类型信息。当 Java 类型擦除在运行时将其擦除为 Transaction 时,它怎么可能知道类型信息?我看到genericTypeParameterizedType 组成,我相信它用于传递类型信息(以解决类型擦除问题)。然而,泽西岛是如何自动填充genericType 的?因为除了上面发布的方法头之外,我从未指定类型信息。

另外,第二个问题:如果 Jersey '自动'知道这么多关于我的类型,我为什么需要 MessageBodyReader 呢?难道没有一些更简单的方法可以将泛型与 Jersey/REST 端点一起使用并使其“正常工作”吗?

【问题讨论】:

  • 实际上,正是因为有MessageBodyReader 实现,它才能神奇地工作。另见stackoverflow.com/questions/19860393/…
  • @Tunaki 我知道,但为什么呢? MessageBodyReader 是如何真正知道类型信息的? (它也是类型擦除 - 对吧?)。
  • 我怀疑这是使用标准技巧:类型擦除会擦除 objects 中的泛型,但不会擦除 classes
  • 这里有一个黑魔法:stackoverflow.com/a/15999255/1743880
  • @Tunaki 我有点明白但不完全。我没有在任何地方创建匿名类。在给出的示例中,作者创建了一个匿名类并将其传递给一个方法。我没有在任何地方这样做。那么我的类型不会在运行时被擦除吗?

标签: java jersey


【解决方案1】:

看看javadoc

genericType - 要生成的实例的类型。例如。如果要将消息体转换为方法参数,这将是Method.getGenericParameterTypes返回的方法参数的正式类型

类、方法和字段中的通用参数化保存在相应的.class 文件中。这显然是编译所必需的。如果不是这种情况,您将无法将 Java 类打包并用作库。

反射公开了这种类型信息(以及更多)。

Jersey 基本上会扫描您的类以查找处理程序方法、使用 @POST 注释的方法等,并检索相应的 Method 对象。一旦有了它,它就可以使用Method#getGenericParameterTypes()

返回代表形参的Type对象数组 按声明顺序,由 this 表示的可执行文件的类型 目的。如果底层可执行文件返回长度为 0 的数组 不带参数。

如果形参类型是参数化类型,Type 对象 为其返回必须准确反映实际类型参数 在源代码中使用。

换句话说,Type 必须同时表示参数的原始类型及其参数化(如果有的话)。

所以 Jersey 会检索这些类型并将它们提供给您的 MessageBodyReader。期望您的实现应该能够处理genericType

我不太了解 Jersey,但如果它遵循与 Spring MVC 相同的策略,它将简单地遍历所有已注册的 MessageBodyReader 实例。它实际上并不关心它的类型(例如您的TransactionMessageBodyReader),这就是它依赖isReadable 的原因。从理论上讲,它可以将类型映射到MessageBodyReader 实现,但这最终变得笨拙且不灵活。无论如何,应用程序通常没有那么多可以序列化的类型/格式,因此迭代几个对象并不是什么大不了的事。


从 Java 8 开始,您还可以访问 Executable#getParametersMethodExecutable 的子类型)。


这是泽西岛可以做的一个例子

public class StackOverflow {
    public static void main(String[] args) throws Exception {
        Method method = StackOverflow.class.getDeclaredMethod("method", List.class);
        Type[] parameterTypes = method.getGenericParameterTypes();
        for (Type parameterType : parameterTypes) {
            System.out.println(parameterType.getClass());
            System.out.println(parameterType);
            ParameterizedType parameterizedType = (ParameterizedType) parameterType;
            System.out
                    .println(parameterizedType.getRawType().getTypeName() + "<" + parameterizedType.getActualTypeArguments()[0].getTypeName() + ">");
        }
    }

    public String method(List<StackOverflow> parameter) {
        return "example";

    }
}

打印

class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
java.util.List<com.example.StackOverflow>
java.util.List<com.example.StackOverflow>

【讨论】:

  • 谢谢。因此,如果 Jersey 可以“看到”所有方法类型,我为什么需要编写 MessageBodyReader 呢?如果没有,Jersey 似乎不知道泛型类型参数。此讨论中的其他发帖者说库使用匿名类之类的技巧来发现类型参数。所以它似乎并不像“它在类文件中所以它可用”那么简单。
  • @KyleM 读取类型并用它做什么? Jersey 可能有一个用于处理 JSON、XML 和标准格式的默认实现。但是,一旦您需要更复杂或更不常见的东西,您就需要自己的实现。
  • 我的观点是,从我上面发布的 checkStatus() 方法来看,Jersey 无法知道泛型类型参数是什么。因此 MessageBodyReader 的必要性...
  • @KyleM 正如答案中所述,它确实有办法。 MessageBodyReader 用于为支持将流转换为 Java 类型的提供者定义 合同。
  • @KyleM 如果您有MyPojo 类型的参数,Jersey 应该在哪里/如何生成该类型的值以调用您的方法?这就是MessageBodyReader 给你的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-09-04
  • 2013-06-02
  • 1970-01-01
  • 2014-09-19
  • 1970-01-01
  • 1970-01-01
  • 2012-08-01
相关资源
最近更新 更多