Jersey 2 中的 ResourceConfig 类到底是啥?

Posted

技术标签:

【中文标题】Jersey 2 中的 ResourceConfig 类到底是啥?【英文标题】:What exactly is the ResourceConfig class in Jersey 2?Jersey 2 中的 ResourceConfig 类到底是什么? 【发布时间】:2018-01-19 10:10:25 【问题描述】:

我看过很多以类似开头的 Jersey 教程

@ApplicationPath("services")
public class JerseyApplication extends ResourceConfig 
    public JerseyApplication() 
        packages("com.abc.jersey.services");
    

没有解释ResourceConfig 类到底是什么。那么我在哪里可以找到它的文档、用法等呢?谷歌搜索“jersey resourceconfig”不会产生任何官方文档。

我对这个类及其用法的一些问题是:

我可以在ResourceConfig 的子类中做些什么? 我是否需要在某处注册ResourceConfig 的子类以便可以找到它或者它会被Jersey 自动检测到? 如果自动检测到子类,如果我有多个 ResourceConfig 子类会怎样? ResourceConfig 的用途与web.xml 文件的用途相同吗?如果是这样,如果我在我的项目中同时拥有这两者会发生什么?其中一个是否优先于另一个?

【问题讨论】:

【参考方案1】:

标准 JAX-RS 使用 Application 作为其配置类。 ResourceConfig 扩展 Application.

有三种主要方式(在 servlet 容器中)来配置 Jersey (JAX-RS):

    只有 web.xml 同时使用 web.xml Application/ResourceConfig 类 只有一个Application/ResourceConfig 类用@ApplicationPath 注释。

只有 web.xml

可以以标准的 JAX-RS 方式配置应用程序,但以下是特定于 Jersey 的

<web-app>
    <servlet>
        <servlet-name>jersey-servlet</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.mypackage.to.scan</param-value>
        </init-param>
    </servlet>
    ...
    <servlet-mapping>
        <servlet-name>jersey-servlet</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
    ...
</web-app>

由于 Jersey 在 servlet 容器中运行,因此 Jersey 应用程序作为 servlet 运行是正确的。处理传入请求的 Jersey Servlet 是 ServletContainer。所以在这里我们将其声明为&lt;servlet-class&gt;。我们还配置了一个 &lt;init-param&gt; 告诉 Jersey 扫描哪些包以查找我们的 @Path@Provider 类,以便它可以注册它们。

在底层,Jersey 实际上会创建一个ResourceConfig 实例,因为它是用来配置应用程序的。然后它将注册它通过包扫描发现的所有类。

同时使用 web.xml 和 Application/ResourceConfig

如果我们想通过ApplicationResourceConfig 子类以编程方式配置我们的应用程序,我们可以通过对上述web.xml 进行一次更改来实现。我们没有设置一个 init-param 来扫描包,而是使用一个 init-param 来声明我们的 Application/ResourceConfig 子类。

<servlet>
    <servlet-name>jersey-servlet</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>com.example.JerseyApplication</param-value>
    </init-param>
    <servlet-mapping>
        <servlet-name>jersey-servlet</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
</servlet>
package com.example;

public class JerseyApplication extends ResourceConfig 
    public JerseyApplication() 
        packages("com.abc.jersey.services");
    

在这里,我们使用 ResourceConfig 子类的完全限定名称配置 init-param javax.ws.rs.Application。而不是使用init-param 告诉Jersey 要扫描哪个包,我们只使用ResourceConfig 的便捷方法packages()

我们还可以使用register()property() 方法来注册资源和提供者,以及配置Jersey 属性。使用property() 方法,可以配置为init-param 的任何内容也可以使用property() 方法进行配置。例如,我们可以不调用packages(),而是这样做

public JerseyApplication() 
    property("jersey.config.server.provider.packages",
             "com.mypackage.to.scan");

只有Application/ResourceConfig

如果没有 web.xml,Jersey 需要一种方法来提供 servlet 映射。我们使用@ApplicationPath 注释来做到这一点。

// 'services', '/services', or '/services/*'
// is all the same. Jersey will change it to be '/services/*'
@ApplicationPath("services")
public class JerseyApplication extends ResourceConfig 
    public JerseyApplication() 
        packages("com.abc.jersey.services");
    

这里用@ApplicationPath,就像我们在web.xml中配置了servlet映射

<servlet-mapping>
    <servlet-name>JerseyApplication</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>

当仅使用 Java 代码进行配置时,Jersey 需要通过某种方式发现我们的配置类。这是通过使用ServletContanerInitializer 完成的。这是 Servlet 3.0 规范中引入的内容,因此我们不能在早期的 servlet 容器中使用“仅限 Java”配置。

基本上,初始化程序的实现者可以告诉 servlet 容器要查找哪些类,然后 servlet 容器会将这些类传递给初始化程序 onStartup() 方法。在 Jersey 的初始化器实现中,Jersey 将其配置为查找 Application 类和带有 @ApplicationPath 注释的类。请参阅this post 了解更多说明。因此,当 servlet 容器启动应用程序时,Jersey 的初始化程序将通过我们的 Application/ResourceConfig 类。

我可以在 ResourceConfig 的子类中做些什么

看看javadoc。它主要只是注册课程。你不需要做太多其他事情。您将使用的主要方法是register()packages()property() 方法。 register() 方法允许您手动注册资源和提供者的类和实例。前面讨论过的packages() 方法列出了您希望Jersey 扫描@Path@Provider 类并为您注册它们的包。而property() 方法允许你设置一些configurable properties 1

ResourceConfig 只是一个便利类。请记住,它扩展了Application,所以我们甚至可以使用标准的Application

@ApplicationPath("/services")
public class JerseyApplication extends Application 

    private final Set<Class<?>> classes;
    private final Set<Object> singletons;

    public JerseyApplication() 
        // configure in constructor as Jersey
        // may call the getXxx methods multiple times

        this.classes = new HashSet<>();
        this.classes.add(MyResource.class);

        this.singletons = new HashSet<>();
        this.singletons.add(new MyProvider());
    

    @Override
    public Set<Class<?>> getClasses() 
        return this.classes;
    

    @Override
    public Set<Object> getSingletons() 
        return this.singletons;
    

    @Override
    public Map<String, Object> getProperties() 
        final Map<String, Object> properties = new HashMap<>();
        properties.put("jersey.config.server.provider.packages",
                       "com.mypackage.to.scan");
        return properties;
    

有了ResourceConfig,我们就可以了

public class JerseyApplication extends ResourceConfig 
    public JerseyApplication() 
        register(MyResource.class);
        register(new MyProvider());
        packages("com.mypackages.to.scan");
    

除了更方便之外,还有一些事情可以帮助 Jersey 配置应用程序。

SE 环境

以上所有示例都假设您在已安装的服务器环境中运行,例如雄猫。但您也可以在 SE 环境中运行应用程序,在其中运行嵌入式服务器并从 main 方法启动应用程序。您有时会在四处搜索信息时看到这些示例,所以我想展示一下它们的外观,这样如果您遇到这种情况,您不会感到惊讶,并且知道它与您的设置有何不同。

所以有时你会看到这样的例子

ResourceConfig config = new ResourceConfig();
config.packages("com.my.package");
config.register(SomeFeature.class);
config.property(SOME_PROP, someValue);

这里最有可能发生的情况是该示例使用了嵌入式服务器,例如 Grizzly。启动服务器的其余代码可能类似于

public static void main(String[] args) 
    ResourceConfig config = new ResourceConfig();
    config.packages("com.my.package");
    config.register(SomeFeature.class);
    config.property(SOME_PROP, someValue);

    String baseUri = "http://localhost:8080/api/";
    HttpServer server = GrizzlyHttpServerFactory
            .createHttpServer(URI.create(baseUri), config);
    server.start();

所以在这个例子中,有一个独立的服务器正在启动,ResourceConfig 用于配置 Jersey。这里和前面的例子不同的是,在这个例子中,我们没有扩展ResourceConfig,而是仅仅实例化它。如果我们这样做也不会有什么不同

public class JerseyConfig extends ResourceConfig 
    public JerseyConfig() 
        packages("com.my.package");
        register(SomeFeature.class);
        property(SOME_PROP, someValue);
    


HttpServer server = GrizzlyHttpServerFactory
            .createHttpServer(URI.create(baseUri), new JerseyConfig());

假设您正在阅读一些教程,它显示了一个独立应用程序的配置,他们在其中实例化了ResourceConfig。但是您在已安装的 servlet 容器中运行您的应用程序,并且一直在使用您扩展 ResourceConfig 的早期配置。那么现在您知道区别是什么以及您需要进行哪些更改。我见过人们做一些非常奇怪的事情,因为他们不理解这种差异。例如,我看到有人在资源类中实例化 ResourceConfig。所以这就是我添加这个额外的小片段的原因;所以你不会犯同样的错误。


脚注

1。有许多不同的可配置属性。 ServerProperties 的链接只是一些一般属性。还有与特定功能相关的不同属性。文档应在与该功能相关的文档部分中提及这些属性。对于所有可配置属性的完整列表,您可以查看所有Jersey constants 并查找字符串值以jersey.config 开头的那些。如果您使用的是 web.xml,那么您将使用字符串值作为 init-param param-name。如果您使用的是 Java 配置 (ResourceConfig),那么您将调用 property(ServerProperties.SOME_CONF, value)

【讨论】:

嗨,保罗,谢谢你的详细回答,我希望你能把我推向正确的方向。请参阅我的问题***.com/questions/70646059/… 如果您使用 web.xml & ResourceConfig 解决方案并且您使用 Jersey 3.x - 您需要将 jakarta.ws.rs.Application 设置为 param-name

以上是关于Jersey 2 中的 ResourceConfig 类到底是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何将Swagger no config设置与Jersey 2集成

JAX-RS (Jersey 2) - 使用 JSR 250 注释的授权

Jersey 2.x JDK 上的客户端应用

Jersey 1 @Inject 迁移到 Jersey 2 停止工作

为 Jersey 代理客户端解析资源接口中的模板变量

Jersey中的会话变量(Java / JBoss)