Jersey 2中的ResourceConfig类到底是什么?

92

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

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

不解释 ResourceConfig 类的具体作用是什么。那么我在哪里可以找到它的文档、用法等呢?谷歌搜索 "jersey resourceconfig" 并没有出现官方文档。

这个类及其用法引发了我的一些问题:

  • ResourceConfig 子类中可以做哪些事情?
  • 我需要在某个地方注册 ResourceConfig 子类,以便 Jersey 可以找到它吗,还是 Jersey 会自动检测到它?
  • 如果子类被自动检测到了,如果我有多个 ResourceConfig 子类会发生什么?
  • ResourceConfig 的目的是否与 web.xml 文件相同?如果是的话,如果我在项目中同时使用两者会发生什么?它们之间是否有优先顺序?
1个回答

227

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

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

  1. 仅使用web.xml
  2. 同时使用web.xml 一个Application/ResourceConfig
  3. 仅使用带有@ApplicationPath注释的Application/ResourceConfig类。

仅使用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。所以这里我们将其声明为<servlet-class>。我们还配置了一个<init-param>,告诉Jersey要扫描哪些包来注册我们的@Path@Provider类。
在底层,Jersey实际上会创建一个ResourceConfig实例,因为它用于配置应用程序。然后它将注册通过包扫描发现的所有类。
如果我们想使用ApplicationResourceConfig子类以编程方式配置我们的应用程序,我们可以对上述web.xml进行一次更改。我们使用一个init-param来声明我们的Application/ResourceConfig子类,而不是设置一个init-param来扫描包。
<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-paramjavax.ws.rs.Application。而且,我们不使用告诉Jersey要扫描哪些包的init-param,我们只是使用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-mapping。我们使用@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环境

上面所有的示例都假设您正在安装的服务器环境中运行,例如Tomcat。但是您也可以在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常量,并查找字符串值以 jersey.config 开头的常量。如果您使用的是 web.xml,则将字符串值用作 init-paramparam-name。如果您使用 Java 配置 (ResourceConfig),则应调用 property(ServerProperties.SOME_CONF, value)


3
如果您使用web.xml和ResourceConfig解决方案,并且使用Jersey 3.x,则需要将jakarta.ws.rs.Application设置为param-name - Franz Deschler
很棒的答案。只是好奇,我们在哪里可以找到有关此的官方文档?所有Jersey链接似乎都已经损坏并指向了一个通用的Jakarta主页。 - Jesse Barnum
1
@JesseBarnum https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/index.html - Paul Samsotha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接