Что такое класс ResourceConfig в Джерси 2?

Я видел много уроков Джерси, которые начинаются с чего-то вроде

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

не объясняя, что такое класс ResourceConfig. Итак, где я могу найти его документацию, использование и т.д.? Googling для "jersey resourceconfig" не дает официального документа.

Некоторые из моих вопросов об этом классе и его использовании:

  • Что я могу сделать в подклассе ResourceConfig?
  • Нужно ли мне регистрировать подкласс ResourceConfig где-нибудь, чтобы его можно было найти или он был автоматически обнаружен Джерси?
  • Если подкласс автоматически определяется, что произойдет, если у меня есть несколько подклассов ResourceConfig?
  • Является ли цель ResourceConfig тем же, что и файл web.xml? Если да, то что произойдет, если у меня есть оба проекта? Один из них имеет приоритет над другим?

Ответ 1

Стандарт JAX-RS использует Application качестве класса конфигурации. ResourceConfig расширяет Application.

Существует три основных способа (в контейнере сервлетов) настроить Джерси (JAX-RS):

  1. Только с web.xml
  2. Как с web.xml, так и с классом Application/ResourceConfig
  3. Только с классом Application/ResourceConfig аннотированным @ApplicationPath.

Только с web.xml

Можно настроить приложение стандартным способом JAX-RS, но следующее характерно для Джерси

<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 запускается в контейнере сервлетов, то совершенно верно, что приложение Jersey работает как сервлет. Сервлет Джерси, который обрабатывает входящие запросы, является ServletContainer. Итак, здесь мы объявляем его как <servlet-class>. Мы также настраиваем <init-param> сообщая Джерси, какой пакет сканировать для наших @Path и @Provider чтобы он мог их зарегистрировать.

На самом деле Джерси создаст экземпляр ResourceConfig, который используется для настройки приложения. Затем он зарегистрирует все классы, обнаруженные при сканировании пакета.

Как с web.xml, так и с Application/ResourceConfig

Если мы хотим программно настроить наше приложение с подклассом Application или ResourceConfig, мы можем сделать это с одним изменением в приведенном выше файле 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");
    }
}

Здесь мы настраиваем init-param javax.ws.rs.Application с полным именем нашего подкласса ResourceConfig. И вместо того, чтобы использовать init-param который сообщает Джерси, какой пакет нужно сканировать, мы просто используем вспомогательный метод packages() ResourceConfig.

Мы также могли бы использовать методы register() и property() для регистрации ресурсов и поставщиков, а также для настройки свойств Джерси. С помощью метода property() все, что можно настроить как init-param, также можно настроить с помощью метода property(). Например, вместо вызова packages(), мы могли бы сделать

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

Только с Application/ResourceConfig

Без web.xml Джерси нужен способ предоставить нам отображение сервлетов. Мы делаем это с @ApplicationPath аннотации @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-mapping>
    <servlet-name>JerseyApplication</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>

При использовании только Java-кода для конфигурации у Джерси должен быть какой-то способ обнаружить наш класс конфигурации. Это делается с использованием ServletContanerInitializer. Это то, что было представлено в спецификации Servlet 3.0, поэтому мы не можем использовать конфигурацию "только Java" в более ранних контейнерах сервлетов.

В основном происходит то, что разработчик инициализатора может сообщить контейнеру сервлета, какие классы искать, и контейнер сервлета передаст эти классы onStartup() инициализатора onStartup(). В реализации - Джерси инициализатора, Джерси настраивает его искать Application классов и классы, аннотированные @ApplicationPath. Смотрите этот пост для дальнейшего объяснения. Поэтому, когда контейнер сервлета запускает приложение, инициализатор Джерси будет передан нашему классу Application/ResourceConfig.

Что я могу сделать внутри подкласса ResourceConfig

Просто посмотрите на Javadoc. В основном это просто регистрация классов. Не так много, что вам нужно сделать с этим. Основными методами, которые вы будете использовать, являются методы register(), packages() и property(). Метод register() позволяет вручную регистрировать классы и экземпляры ресурсов и поставщиков. Метод packages(), рассмотренный ранее, перечисляет пакеты, которые вы хотите, чтобы Джерси сканировал для @Path и @Provider и регистрировал их для вас. А метод property() позволяет вам установить некоторые настраиваемые свойства 1.

ResourceConfig - это просто удобный класс. Помните, это расширяет Application, поэтому мы могли бы даже использовать стандартный класс Application

@ApplicationPath("/services")
public class JerseyApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        final Set<Class<?>> classes = new HashSet<>();
        classes.add(MyResource.class);
        return classes;
    }
    @Override
    public Set<Object> getSingletons() {
        final Set<Object> singletons = new HashSet<>();
        singletons.add(new MyProvider());
        return 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");
    }
}

Помимо удобства, есть еще несколько вещей, которые помогают Джерси настроить приложение.

Экологическая среда

Все приведенные выше примеры предполагают, что вы работаете в установленной серверной среде, например 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 используется для настройки Джерси. Здесь и от предыдущих примеров отличается то, что в этом примере мы не расширяем 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, но вы запускаете свое приложение в контейнере сервлета и используете более раннюю конфигурацию, в которой вы расширяете ResourceConfig. Ну, теперь вы знаете, в чем разница и какие изменения вам нужно внести. Я видел людей, делающих действительно странные вещи, потому что они не понимали эту разницу. Например, я видел, как кто-то создает экземпляр ResourceConfig внутри класса ресурса. Вот почему я добавил этот маленький кусочек; так что вы не делаете ту же ошибку.


Сноски

1. Существует ряд различных настраиваемых свойств.Ссылка на ServerProperties - это лишь некоторые общие свойства.Есть также различные свойства, связанные с конкретными функциями.В документации должны быть упомянуты эти свойства в разделе документации, относящейся к этой функции.Для получения полного списка всех настраиваемых свойств вы можете посмотреть все константы Джерси и найти те, в которых строковое значение начинается с jersey.config.Если вы используете web.xml, вы должны использовать строковое значение в качестве имени параметра init-param param-name.Если вы используете конфигурацию Java (ResourceConfig), то вы бы вызвали property(ServerProperties.SOME_CONF, value)