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

时间:2018-05-15 08:03:26

标签: java jax-rs

我为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.MessageBodyReaderMETA-INF/services/javax.ws.rs.ext.MessageBodyWriter个文件,这些文件似乎也注册了一个实现接口的类。

所以我的问题是:

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

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

  • 哪些权威文档会对我有所了解?

1 个答案:

答案 0 :(得分:1)

  

IntelliJ IDEA是否正确或错误地抱怨我没有实施javax.ws.rs.ext.Providers

使用create extensible applicationsMETA-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服务的确切模式。

  

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

使用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 specificationJersey documentation

中找到

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

2 - 见How to use Jersey as JAX-RS implementation without web.xml?