jersey
Инъекция зависимостей с Джерси
Поиск…
Базовая инъекция зависимостей с использованием HK2 Джерси
Джерси (2) использует HK2 как систему впрыскивания зависимостей (DI). Мы можем использовать другие системы впрыска, но его инфраструктура построена с помощью HK2 и позволяет нам также использовать ее в наших приложениях.
Настройка простой инъекции зависимостей с помощью Джерси занимает всего несколько строк кода. Скажем, например, у нас есть услуга, которую мы хотели бы внедрить в наши ресурсы.
public class GreetingService {
public String getGreeting(String name) {
return "Hello " + name + "!";
}
}
И мы хотим внедрить эту услугу в ресурс Джерси
@Path("greeting")
public class GreetingResource {
@Inject
public GreetingService greetingService;
@GET
public String get(@QueryParam("name") String name) {
return this.greetingService.getGreeting(name);
}
}
Для того, чтобы инъекция работала, нам нужна простая конфигурация
@ApplicationPath("/api")
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(GreetingResource.class);
register(new AbstractBinder() {
@Override
protected void configure() {
bindAsContract(GreetingService.class);
}
});
}
}
Здесь мы говорим, что хотим привязать GreetingService
к системе инъекций и объявить ее как инъекционную для того же класса. Что означает последнее утверждение, так это то, что мы можем вводить его только как GreetingService
и (возможно, очевидно) не каким-либо другим классом. Как вы увидите позже, это можно изменить.
Вот и все. Это все, что вам нужно. Если вы не знакомы с этой конфигурацией ResourceConfig
(возможно, вы используете web.xml), просьба ознакомиться с настройкой JAX-RS в теме Джерси в SO Docs.
Примечание: инъекция выше - инъекция поля, где услуга вводится в поле ресурса. Другим типом инъекции является инъекция конструктора, где услуга вводится в конструктор
private final GreetingService greetingService;
@Inject
public GreetingResource(GreetingService greetingService) {
this.greetingService = greetingService;
}
Вероятно, это предпочтительный вариант, в отличие от полевой инъекции, поскольку он облегчает модульный тест. Инъекция конструктора не требует никакой другой конфигурации.
Ok теперь позволяет сказать, что вместо класса GreetingService
является интерфейсом, и у нас есть его реализация (что очень распространено). Чтобы настроить это, мы использовали бы следующий синтаксис в приведенном выше методе configure
@Override
protected void configure() {
bind(NiceGreetingService.class).to(GreetingService.class);
}
Это читается как «связывать NiceGreetingService
и рекламировать его как GreetingService
». Это означает, что мы можем использовать тот же самый код в GreetingResource
выше, потому что мы рекламируем контракт как GreetingService
а не NiceGreetingService
. Но фактическая реализация при введении будет NiceGreetingService
.
Теперь о сфере. Если вы когда-либо работали с любой инфраструктурой инъекций, вы столкнулись с концепцией сферы действия, которая определяет срок службы службы. Возможно, вы слышали о «области запроса», где услуга жива только для жизни запроса. Или «Singleton Scope», где есть только один экземпляр службы. Мы также можем настроить эти области применения, используя следующий синтаксис.
@Override
protected void configure() {
bind(NiceGreetingService.class)
.to(GreetingService.class)
.in(RequestScoped.class);
}
Область по умолчанию - PerLookup
, что означает, что каждый раз, когда запрашивается эта услуга, создается новая. В примере выше, используя RequestScoped
, новая служба будет создана для одного запроса. Это может быть или не быть таким же, как PerLookup
, в зависимости от того, сколько мест мы пытаемся ввести. Возможно, мы попытаемся ввести его в фильтр и в ресурс. Если это PerLookup
, то для каждого запроса будут созданы два экземпляра. В этом случае мы хотим только одного.
Доступны две другие области: Singleton
(только один экземпляр создан) и Immediate
(например, Singleton
), но создается при запуске (тогда как с Singleton
он не создается до первого запроса).
Помимо связывания классов, мы могли бы просто использовать экземпляр. Это даст нам стандартный синглтон, поэтому нам не нужно использовать синтаксис in
.
@Override
protected void configure() {
bind(new NiceGreetingService())
.to(GreetingService.class);
}
Что делать, если у нас есть сложная логика создания или нужна информация о запросе запроса для службы. В этом случае есть Factory
s. Большинство вещей, которые мы можем вводить в наши ресурсы в Джерси, мы также можем вводить в Factory
. Возьмем например
public class GreetingServiceFactory implements Factory<GreetingService> {
@Context
UriInfo uriInfo;
@Override
public GreetingService provide() {
return new GreetingService(
uriInfo.getQueryParameters().getFirst("name"));
}
@Override
public void dispose(GreetingService service) {
/* noop */
}
}
Здесь у нас есть фабрика, которая получает информацию запроса от UriInfo
, в этом случае параметры запроса, и мы создаем GreetingService
из нее. Чтобы настроить его, мы используем следующий синтаксис
@Override
protected void configure() {
bindFactory(GreetingServiceFactory.class)
.to(GreetingService.class)
.in(RequestScoped.class);
}
Вот и все. Это всего лишь основы. Есть еще много вещей, которые делают HK и Джерси.