注册MessageBodyReader和MessageBodyWriter实现的适当位置是什么?

我为com.ca.tas.crypto.cmp.client.GeneralPKIMessageJaxRsReader实现了Javax WS RS API MessageBodyReadercom.ca.tas.crypto.cmp.client.GeneralPKIMessageJaxRsReader MessageBodyWriter ,这样我就可以轻松地在CMP over HTTP REST API中使用这些类型。 现在,要注册我创建META-INF/services/javax.ws.rs.ext.Providers文件的类型并将类名放在那里。 一切正常,我可以使用API​​进行REST调用,除了:

  • IntelliJ IDEA(或我安装到其中的插件之一)抱怨说

    注册的扩展应该实现javax.ws.rs.ext.Providers

    在文件中的两行。 基于我在互联网上找到的资源,我认为添加@Provider@Produces("application/pkixcmp")注释就足够了。

  • 我注意到FasterXML Jackson有META-INF/services/javax.ws.rs.ext.MessageBodyReaderMETA-INF/services/javax.ws.rs.ext.MessageBodyWriter文件,这些文件似乎注册了一个实现接口的类同样。

所以我的问题是:

  • IntelliJ IDEA是正确还是错误,抱怨我没有实现javax.ws.rs.ext.Providers

  • 注册MessageBodyReaderMessageBodyWriter实现的正确文件是什么?

  • 什么是权威文档可以启发我这个?

IntelliJ IDEA是正确还是错误,抱怨我没有实现javax.ws.rs.ext.Providers

META-INF/services中的文件是我们通过使用ServiceLoader 创建可扩展应用程序的一部分。 它的工作原理是文件名应该是合同的名称,文件的内容应该是该合同的实现列表。 然后, ServiceLoader将查看该文件并收集所有实现。 所以使用ServiceLoader ,我们可以做到

 ServiceLoader readersLoader = ServiceLoader.load(MessageBodyReader.class); 

基于传递给load方法的类,Java将搜索该文件

 META-INF/services/javax.ws.rs.ext.MessageBodyReader 

并查看该文件的内容以查找它应加载的所有实现。

因此,基于该信息,您可以看到IntelliJ在抱怨中正确的,因为您的读者和作者没有正确实现javax.ws.rs.ext.Providers

我应该指出的一点是,我不认为ServiceLoader类是直接使用的,因为它要求服务实现具有无参数构造函数。 但这是用于META服务的确切模式。

注册MessageBodyReaderMessageBodyWriter实现的正确文件是什么?

META-INF/services文件的使用不是JAX-RS规范的一部分。 这是一个特定于JAX-RS实现的实现细节,尽管这种模式被大量使用。 您将主要看到可重用库中使用的文件,例如您提到的Jackson库1

如果提供者将成为我们应用程序的一部分,那么有更常见的方式来注册它。

  • 您提到的@Provider注释是一个标记注释,用于检测应注册的提供者类。 启用扫描后,运行时将扫描使用@Provider注释的类,然后将其与应用程序一起注册。

    当我们谈论扫描时,有几种不同的方式:类路径扫描和包扫描。 通过使用@ApplicationPath注释的空Application类,在JAX-RS应用程序中启用类路径扫描。

     @ApplicationPath("/api/*") public class ApplicationConfig extends Application {} 

    这足以让JAX-RS应用程序配置2 。 将启用类路径扫描,它将扫描使用@Path@Provider注释的所有类的整个类路径并注册这些类。

    扫描是针对Jersey实现的特定内容。 我们可以这样配置我们的应用程序

     @ApplicationPath("api") public class ApplicationConfig extends ResourceConfig { public ApplicationConfig() { package("the.package.to.scan"); } } 

    在这里,我们告诉Jersey扫描the.package.to.scan@Provider类的the.package.to.scan包以注册该应用程序。

  • 注册我们的提供者的另一种方法是明确注册它们。 在Application子类中,您将覆盖getClasses()getSingletons()以分别将它们注册为类或对象。

     @ApplicationPath("/api/*") public class ApplicationConfig extends Application { private final Set> classes = new HashSet<>(); private final Set singletons = new HashSet<>(); public ApplicationConfig() { classes.add(MyMessageBodyReader.class); singletons.add(new MyMessageBodyReader()); } @Override public Set> getClasses() { return this.classes; } @Override public Set getSingletons() { return this.singletons; } } 

    请注意,一旦覆盖这些方法中的任何一个并返回非空集,就会自动禁用类路径扫描,并且您需要手动注册所有内容。

    如果您正在使用Jersey实现,那么我们还可以通过Jersey特定方式明确注册资源和提供程序。 有关这方面的更多讨论,请参阅Jersey 2中的ResourceConfig类究竟是什么? 。

  • 我可以考虑注册提供者的另一种方法是使用function。 我们可以使用vanilla Feature ,也可以使用DynamicFeature

    使用Feature ,我们在整个应用程序中注册提供程序

     // We should register the feature with our application public class MyFeature implements Feature { @Override public boolean configure(FeatureContext context) { context.register(MyMessageBodyReader.class); } } 

    使用DynamicFeature我们可以选择性地使用特定的资源方法或资源类注册提供程序。 有关动态绑定的详情,请参阅Jersey文档 。 应该注意的是,动态绑定更多地用于filter和拦截器(这也是术语,提供者的一般意义上的),而不是实体提供者(MessageBodyReader / Writers)。

  • 可能还有其他方式来注册您的提供程序,但上面提到的是您将在应用程序中看到它的主要方式。

什么是权威文档可以启发我这个?

我不确定在任何文档中有多少关于META-INF /服务文件的信息。 但是显式注册和类路径扫描,您可能会在JAX-RS规范或Jersey文档中找到


1 – 应该注意的是,仅仅因为文件存在,并不意味着它将被使用。 无论是否使用它,都取决于JAX-RS的实现。 例如,Jersey不会在MessageBodyReader和编写器上使用它。

2 – 请参阅如何在没有web.xml的情况下将Jersey用作JAX-RS实现?