我为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
在文件中的两行。基于我在互联网上找到的资源,我认为添加@Provider
和@Produces("application/pkixcmp")
注释就足够了。
我注意到FasterXML Jackson有META-INF/services/javax.ws.rs.ext.MessageBodyReader
和META-INF/services/javax.ws.rs.ext.MessageBodyWriter
个文件,这些文件似乎也注册了一个实现接口的类。
所以我的问题是:
IntelliJ IDEA是正确还是错误,抱怨我没有实施javax.ws.rs.ext.Providers
?
注册MessageBodyReader
和MessageBodyWriter
实施的正确文件是什么?
哪些权威文档会对我有所了解?
答案 0 :(得分:1)
IntelliJ IDEA是否正确或错误地抱怨我没有实施
javax.ws.rs.ext.Providers
?
使用create extensible applications,META-INF/services
中的文件是我们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实现的实现细节,尽管这种模式被大量使用。您将主要看到可重用库中使用的文件,例如您提到的 1 的Jackson库。
如果提供者将成为我们应用程序的一部分,那么有更常见的方式来注册它。
您提到的@Provider
注释是用于检测应注册的提供程序类的标记注释。启用扫描后,运行时将扫描使用@Provider
注释的类,然后将其与应用程序一起注册。
当我们谈论扫描时,有几种不同的方式:类路径扫描和包扫描。通过使用Application
注释的空@ApplicationPath
类,在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");
}
}
在这里,我们告诉泽西岛扫描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
和作家上使用它。
2 - 见How to use Jersey as JAX-RS implementation without web.xml?