我为MessageBodyReader
实现了Javax WS RS API com.ca.tas.crypto.cmp.client.GeneralPKIMessageJaxRsReader
,为MessageBodyWriter
实现了org.bouncycastle.asn1.cmp.PKIMessage
,因此我可以轻松使用CMP over HTTP REST API中的类型。现在,要注册我创建的META-INF/services/javax.ws.rs.ext.Providers
文件的类型并将类名放在此处。一切正常,我可以使用API进行REST调用,但以下情况除外:
IntelliJ IDEA(或我已安装到其中的插件之一)抱怨
注册的扩展名应实现javax.ws.rs.ext.Providers
在文件的两行上。根据我在Internet上某处发现的资源,我认为添加@Provider
和@Produces("application/pkixcmp")
批注就足够了。
我注意到FasterXML Jackson具有META-INF/services/javax.ws.rs.ext.MessageBodyReader
和META-INF/services/javax.ws.rs.ext.MessageBodyWriter
文件,这些文件似乎也注册了一个实现接口的类。
所以我的问题是:
抱怨我未实现javax.ws.rs.ext.Providers
时,IntelliJ IDEA是正确还是错误?
注册MessageBodyReader
和MessageBodyWriter
实现的正确文件是什么?
什么能启发我的权威文献?
最佳答案
抱怨我未实现javax.ws.rs.ext.Providers
时,IntelliJ IDEA是正确还是错误?META-INF/services
中的文件是我们通过使用create extensible applications进行ServiceLoader
方式的一部分。它的工作方式是,文件名应为合同的名称,文件的内容应为该合同的实现列表。 ServiceLoader
然后将看到该文件并收集所有实现。因此,使用ServiceLoader
,我们可以
ServiceLoader<MessageBodyReader> readersLoader
= ServiceLoader.load(MessageBodyReader.class);
根据传递给
load
方法的类,Java将搜索文件META-INF/services/javax.ws.rs.ext.MessageBodyReader
并查看该文件的内容以查找该文件应加载的所有实现。
因此,根据这些信息,您可以看到IntelliJ在抱怨中是正确的,因为您的阅读器和编写器没有正确实现
javax.ws.rs.ext.Providers
。我应该指出的一件事是,我不认为直接使用
ServiceLoader
类是因为它要求服务实现具有无参数构造函数。但这是用于META服务的确切模式。注册
MessageBodyReader
和MessageBodyWriter
实现的正确文件是什么?META-INF/services
文件的使用不是JAX-RS规范的一部分。这是一个实现细节,将专用于JAX-RS实现,尽管这种模式使用了很多。您将大体上看到可重用库中使用的文件,例如您提到的Jackson库1。如果提供者将成为我们应用程序的一部分,那么可以使用更通用的方式进行注册。
您提到的
@Provider
注释是标记注释,用于检测应注册的提供程序类。启用扫描后,运行时将扫描用@Provider
注释的类,然后它将在应用程序中注册它们。当我们谈论扫描时,有两种不同的方式:类路径扫描和包扫描。通过使用以
Application
注释的空@ApplicationPath
类,可以在JAX-RS应用程序中启用类路径扫描。@ApplicationPath("/api/*")
public class ApplicationConfig extends Application {}
这足以配置JAX-RS应用程序。将启用类路径扫描,它将扫描整个类路径以查找所有用
@Path
和@Provider
注释的类,并注册这些类。软件包扫描是特定于Jersey实施的内容。我们可以这样配置我们的应用程序
@ApplicationPath("api")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
package("the.package.to.scan");
}
}
在这里,我们告诉Jersey扫描
the.package.to.scan
包中的@Path
和@Provider
类,以向应用程序注册。注册我们的提供程序的另一种方法是显式注册他们。在
Application
子类中,您将覆盖getClasses()
或getSingletons()
分别将它们注册为类或对象。@ApplicationPath("/api/*")
public class ApplicationConfig extends Application {
private final Set<Class<?>> classes = new HashSet<>();
private final Set<Object> singletons = new HashSet<>();
public ApplicationConfig() {
classes.add(MyMessageBodyReader.class);
singletons.add(new MyMessageBodyReader());
}
@Override
public Set<Class<?>> getClasses() {
return this.classes;
}
@Override
public Set<Object> getSingletons() {
return this.singletons;
}
}
请注意,一旦您覆盖了这两种方法中的任何一个并返回了一个非空集,则将自动禁用类路径扫描,并且您将需要手动注册所有内容。
如果您使用的是Jersey实施,则还有一些我们可以明确注册资源和提供程序的特定于Jersey的方法。有关此内容的更多讨论,请参见What exactly is the ResourceConfig class in Jersey 2?。
我可以考虑注册提供者的另一种方法是使用功能。我们可以使用香草
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 docs for dynamic binding中查看更多信息。应该注意的是,动态绑定更多地用于过滤器和拦截器(在通常的意义上,提供者),而不是实体提供者(MessageBodyReader / Writers)。可能还有其他注册提供商的方法,但是上面提到的是您将看到它在应用程序中完成的主要方法。
什么能启发我的权威文献?
我不确定任何文档中有多少关于META-INF /服务文件的信息。但是显式注册和类路径扫描,您可能会在JAX-RS specification或Jersey documentation中找到
1-应该注意的是,仅仅因为文件存在,并不意味着它将被使用。是否关心是否使用JAX-RS取决于它。例如,Jersey不会在
MessageBodyReader
和writers上使用它。2-参见How to use Jersey as JAX-RS implementation without web.xml?
关于java - 注册MessageBodyReader和MessageBodyWriter实现的适当位置是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50345202/