注册MessageBodyReader和MessageBodyWriter实现的适当位置是什么? [英] What is the proper place to register MessageBodyReader and MessageBodyWriter implementations?

查看:101
本文介绍了注册MessageBodyReader和MessageBodyWriter实现的适当位置是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经为 com.ca.tas.crypto.cmp.client.GeneralPKIMessageJaxRsReader MessageBodyReader >和 MessageBodyWriter for 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 implementation?


  • 哪些权威文档可以让我了解这个?



解决方案


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


META-INF / services 中的文件是我们通过使用 docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.htmlrel =nofollow noreferrer> ServiceLoader 。它的工作原理是文件名应该是合同的名称,文件的内容应该是该合同的实现的列表。然后 ServiceLoader 将查看该文件并收集所有实现。因此,使用 ServiceLoader ,我们可以

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

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

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

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



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



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


注册<$ c $的正确文件是什么c> MessageBodyReader 和 MessageBodyWriter 实现?


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



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




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



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

      @ApplicationPath(/ api / *)
    公共类ApplicationConfig扩展Application {}

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



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

      @ApplicationPath(api)
    公共类ApplicationConfig扩展ResourceConfig {
    public ApplicationConfig(){
    package(the.package.to.scan);
    }
    }

    在这里,我们告诉泽西岛扫描 the.package.to.scan @Path @Provider 要在应用程序中注册的类。


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

      @ApplicationPath(/ api / *) 
    公共类ApplicationConfig扩展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(){
    返回this.classes;
    }

    @Override
    public Set< Object> getSingletons(){
    返回this.singletons;
    }
    }

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



    如果您使用Jersey实现,还有Jersey特定的我们可以明确注册资源和提供者的方式。有关此问题的更多讨论,请参阅 Jersey 2中的ResourceConfig类究竟是什么?


  • 我认为注册提供商的另一种方式是使用功能。我们可以使用香草 功能 或我们可以使用 DynamicFeature



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

      //我们应该在我们的网站上注册该功能application 
    public class MyFeature实现Feature {
    @Override
    public boolean configure(FeatureContext context){
    context.register(MyMessageBodyReader.class);
    }
    }

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


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





哪些权威文档可以让我了解这个?


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






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



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


I have implemented Javax WS RS API MessageBodyReader for com.ca.tas.crypto.cmp.client.GeneralPKIMessageJaxRsReader and MessageBodyWriter for org.bouncycastle.asn1.cmp.PKIMessage so that I can easily use the types in CMP over HTTP REST API. Now, to register the types I have created META-INF/services/javax.ws.rs.ext.Providers file and put the class names there. Everything works fine, I can do REST calls using the API, except:

  • IntelliJ IDEA (or one of the plugins I have installed into it) complains that

    Registered extension should implement javax.ws.rs.ext.Providers

    on the two lines in the file. Based on the resources I have found somewhere on the Internet I thought adding @Provider and @Produces("application/pkixcmp") annotations should be enough.

  • I have noticed that FasterXML Jackson has META-INF/services/javax.ws.rs.ext.MessageBodyReader and META-INF/services/javax.ws.rs.ext.MessageBodyWriter files and those seem to register a class that implements the interfaces as well.

So my questions are:

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

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

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

解决方案

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

The files in the META-INF/services are part of the way we can create extensible applications by making use of the ServiceLoader. How it works is that the filename should be the name of the contract and the contents of the file should be a list of implementations of that contract. The ServiceLoader will then see the file and collect all the implementations. So using the ServiceLoader, we could do

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

Based on the class passed to the load method, Java will search for the file

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

and look at the contents of that file to find all the implementations that it should load.

So based on that information, you can see that IntelliJ is correct in complaining, as your reader and writer do not correctly implement javax.ws.rs.ext.Providers.

One thing I should point out is that I don't think that the ServiceLoader class is used directly as it requires that the service implementations have no-arg constructors. But this is the exact pattern that is used in regards to the META services.

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

The use of META-INF/services files is not something that is a part of the JAX-RS specification. This is an implementation detail that will be specific to the JAX-RS implementation, though this pattern is used a lot. You will mostly see the files being used in reusable libraries, such as the Jackson library you mentioned1.

If the provider is going to be a part of our application, then there are more common ways in which to register it.

  • The @Provider annotation as you mentioned is a marker annotation to detect a provider class that should be registered. When scanning is enabled, the runtime scans for classes annotated with @Provider and then it will register them with the application.

    When we talk about scanning, there are a couple different ways: classpath scanning and package scanning. Classpath scanning is enabled in a JAX-RS application by having an empty Application class that is annotated with @ApplicationPath.

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

    This is enough to get a JAX-RS application configured2. Classpath scanning will be enabled, which will scan the entire classpath for all classes annotated with @Path and @Provider and register those classes.

    Package scanning is something that is specific to the Jersey implementation. We could configure our application as such

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

    Here, we are telling Jersey to scan the the.package.to.scan package for @Path and @Provider classes to register with the application.

  • Another way to register our providers is to explicitly register them. In Application subclass, you would override getClasses() or getSingletons() to register them as a class or as an object, respectively.

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

    Note that once you override either of these methods and return a non-empty set, the classpath scanning is automatically disabled, and you will need to register everything manually.

    If you are using the Jersey implementation, there are also Jersey specific ways that we can explicitly register resources and providers. For more discussion on this, see What exactly is the ResourceConfig class in Jersey 2?.

  • Another way I can think to register providers is through the use of features. We can use the vanilla Feature or we can use a DynamicFeature.

    With the Feature, we register the provider with the entire application

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

    With a DynamicFeature we can selectively register a provider with specific resource methods or resource classes. See more in the Jersey docs for dynamic binding. It should be noted that dynamic binding is more used with filters and interceptors (which are also in the general sense of the term, providers) and not so much with entity providers (MessageBodyReader/Writers).

  • There may be other ways to register your providers, but the one mentioned above are the main ways that you will see it being done in an application.

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

I'm not sure how much information about META-INF/service files in any documentation. But explicit registration and classpath scanning, you will probably find in the JAX-RS specification or Jersey documentation


1 - It should be noted that just because the file is there, it does not mean that it will be used. It is up to the JAX-RS implementation whether or not they care to use it. For Example, Jersey will not use it on MessageBodyReaders and writers.

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

这篇关于注册MessageBodyReader和MessageBodyWriter实现的适当位置是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆