注册 MessageBodyReader 和 MessageBodyWriter 实现的正确位置是什么?

What is the proper place to register MessageBodyReader and MessageBodyWriter implementations?

我已经为 com.ca.tas.crypto.cmp.client.GeneralPKIMessageJaxRsReader 实现了 Javax WS RS API MessageBodyReader,为 org.bouncycastle.asn1.cmp.PKIMessage 实现了 MessageBodyWriter,这样我就可以轻松地使用 [=] 中的类型23=] 休息 API。现在,要注册我创建的类型 META-INF/services/javax.ws.rs.ext.Providers 文件并将 class 名称放在那里。一切正常,我可以使用 API 进行 REST 调用,除了:

所以我的问题是:

Is the IntelliJ IDEA correct or wrong to complain about me not implementing javax.ws.rs.ext.Providers?

META-INF/services 中的文件是我们可以create extensible applications by making use of the ServiceLoader 的一部分。它的工作原理是文件名应该是 合同 的名称,文件的内容应该是该合同的 实现 的列表。然后 ServiceLoader 将查看该文件并收集所有实现。所以使用ServiceLoader,我们可以做到

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

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

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

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

因此,根据该信息,您可以看到 IntelliJ 的抱怨是 正确的,因为您的 reader 和作者没有正确实现 javax.ws.rs.ext.Providers

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

What is the correct file to register the MessageBodyReader and MessageBodyWriter implementations?

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

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

  • 您提到的 @Provider 注释是一个标记注释,用于检测应注册的提供程序 class。启用扫描后,运行时会扫描带有 @Provider 注释的 classes,然后将它们注册到应用程序。

    当我们谈论扫描时,有两种不同的方式:class路径扫描和包扫描。通过使用 @ApplicationPath.

    注释的空 Application class 在 JAX-RS 应用程序中启用类路径扫描
    @ApplicationPath("/api/*")
    public class ApplicationConfig extends Application {}
    

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

    Package 扫描是特定于 Jersey 实现的。我们可以这样配置我们的应用程序

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

    在这里,我们告诉 Jersey 扫描 the.package.to.scan 包以查找 @Path@Provider classes 以在应用程序中注册。

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

    @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;
        }
    }
    

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

    如果您使用的是 Jersey 实现,我们还可以通过特定于 Jersey 的方式显式注册资源和提供程序。有关这方面的更多讨论,请参阅 .

  • 我认为注册提供商的另一种方法是使用功能。我们可以使用香草 Feature or we can use a 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 我们可以有选择地注册具有特定资源方法或资源 class 的提供者。在 Jersey docs for dynamic binding 中查看更多信息。应该注意的是,动态绑定更多地与过滤器和拦截器(这也是术语的一般意义上的提供者)一起使用,而不是与实体提供者(MessageBodyReader/Writers)一起使用。

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

What is the authoritative documentation that would enlighten me about this?

我不确定在任何文档中有多少关于 META-INF/service 文件的信息。但是显式注册和class路径扫描,你可能会在JAX-RS specification or Jersey documentation

中找到

1 - 需要注意的是,文件在那里,并不意味着它会被使用。他们是否愿意使用它取决于 JAX-RS 实现。例如,Jersey 不会在 MessageBodyReaders 和 writers 上使用它。

2 - 请参阅