Инъекционная инъекция с Джерси 2.0
Начиная с нуля без какого-либо знания в прошлом году от Джерси 1.x, мне трудно понять, как настроить инъекцию зависимостей в проекте Jersey 2.0.
Я также понимаю, что HK2 доступен в Джерси 2.0, но я не могу найти документы, которые помогают интеграции с Джерси 2.0.
@ManagedBean
@Path("myresource")
public class MyResource {
@Inject
MyService myService;
/**
* Method handling HTTP GET requests. The returned object will be sent
* to the client as "text/plain" media type.
*
* @return String that will be returned as a text/plain response.
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/getit")
public String getIt() {
return "Got it {" + myService + "}";
}
}
@Resource
@ManagedBean
public class MyService {
void serviceCall() {
System.out.print("Service calls");
}
}
pom.xml
<properties>
<jersey.version>2.0-rc1</jersey.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jersey-bom</artifactId>
<version>${jersey.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey</groupId>
<artifactId>jax-rs-ri</artifactId>
</dependency>
</dependencies>
Я могу заставить контейнер запускать и обслуживать мой ресурс, но как только я добавляю @Inject в MyService, структура генерирует исключение:
SEVERE: Servlet.service() for servlet [com.noip.MyApplication] in context with path [/jaxrs] threw exception [A MultiException has 3 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.noip.MyResource errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on com.noip.MyResource
] with root cause
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=MyService,parent=MyResource,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,1039471128)
at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74)
Мой стартовый проект доступен в GitHub: https://github.com/donaldjarmstrong/jaxrs
Ответы
Ответ 1
Вам нужно определить AbstractBinder
и зарегистрировать его в приложении JAX-RS. Связующее определяет, как инъекция зависимости должна создавать ваши классы.
public class MyApplicationBinder extends AbstractBinder {
@Override
protected void configure() {
bind(MyService.class).to(MyService.class);
}
}
Когда @Inject
обнаруживается в параметре или поле типа MyService.class
, он создается с использованием класса MyService
. Чтобы использовать это связующее, его необходимо зарегистрировать в приложении JAX-RS. В вашем web.xml
определите приложение JAX-RS следующим образом:
<servlet>
<servlet-name>MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.mypackage.MyApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyApplication</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Внедрите класс MyApplication
(указанный выше в init-param
).
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new MyApplicationBinder());
packages(true, "com.mypackage.rest");
}
}
В конструкторе класса регистрируется связующее, определяющее инъекцию зависимостей, и мы также сообщаем приложению, где найти ресурсы REST (в вашем случае, MyResource
) с помощью вызова метода packages()
.
Ответ 2
Сначала просто ответьте на комментарий в ответе accepts.
"Что делает привязка? Что делать, если у меня есть интерфейс и реализация?"
Он просто читает bind( implementation ).to( contract )
. Вы можете использовать альтернативную цепочку .in( scope )
. Объем по умолчанию PerLookup
. Поэтому, если вы хотите синглтон, вы можете
bind( implementation ).to( contract ).in( Singleton.class );
Также имеется RequestScoped
Кроме того, вместо bind(Class).to(Class)
вы также можете bind(Instance).to(Class)
, который будет автоматически одинарным.
Добавление в принятый ответ
Для тех, кто пытается выяснить, как зарегистрировать реализацию AbstractBinder
в вашем web.xml(т.е. вы не используете ResourceConfig
), кажется, что связующее не будет обнаружено через сканирование пакетов, то есть
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>
your.packages.to.scan
</param-value>
</init-param>
Или это либо
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.foo.YourBinderImpl
</param-value>
</init-param>
Чтобы заставить его работать, мне пришлось реализовать Feature
:
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
@Provider
public class Hk2Feature implements Feature {
@Override
public boolean configure(FeatureContext context) {
context.register(new AppBinder());
return true;
}
}
Аннотация @Provider
должна позволять отображать Feature
при сканировании пакета. Или без сканирования пакетов вы можете явно зарегистрировать Feature
в web.xml
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.foo.Hk2Feature
</param-value>
</init-param>
...
<load-on-startup>1</load-on-startup>
</servlet>
См. также:
и для общей информации из документации Джерси
UPDATE
Заводы
Помимо основного связывания в принятом ответе, у вас также есть заводы, где вы можете иметь более сложную логику создания, а также иметь доступ к информации контекста запроса. Например
public class MyServiceFactory implements Factory<MyService> {
@Context
private HttpHeaders headers;
@Override
public MyService provide() {
return new MyService(headers.getHeaderString("X-Header"));
}
@Override
public void dispose(MyService service) { /* noop */ }
}
register(new AbstractBinder() {
@Override
public void configure() {
bindFactory(MyServiceFactory.class).to(MyService.class)
.in(RequestScoped.class);
}
});
Затем вы можете ввести MyService
в свой класс ресурсов.
Ответ 3
Выбранный ответ датируется некоторое время назад. Нецелесообразно объявлять каждую привязку в обычном связующем HK2.
Я использую Tomcat, и мне просто нужно добавить одну зависимость. Несмотря на то, что он был разработан для Glassfish, он идеально вписывается в другие контейнеры.
<dependency>
<groupId>org.glassfish.jersey.containers.glassfish</groupId>
<artifactId>jersey-gf-cdi</artifactId>
<version>${jersey.version}</version>
</dependency>
Убедитесь, что контейнер правильно настроен (см. документацию).
Ответ 4
Поздно, но я надеюсь, что это поможет кому-то.
У меня JAX RS определяется следующим образом:
@Path("/examplepath")
@RequestScoped //this make the diference
public class ExampleResource {
Тогда, в моем коде, наконец, я могу ввести:
@Inject
SomeManagedBean bean;
В моем случае SomeManagedBean
является ApplicationScoped bean.
Надеюсь, это поможет кому угодно.
Ответ 5
Oracle рекомендует добавлять аннотацию @Path ко всем типам, которые нужно вводить при объединении JAX-RS с CDI:
http://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced004.htm
Хотя это далеко не идеально (например, вы получите предупреждение от Джерси при запуске), я решил воспользоваться этим маршрутом, что избавит меня от поддержки всех поддерживаемых типов внутри связующего.
Пример:
@Singleton
@Path("singleton-configuration-service")
public class ConfigurationService {
..
}
@Path("my-path")
class MyProvider {
@Inject ConfigurationService _configuration;
@GET
public Object get() {..}
}
Ответ 6
Если вы предпочитаете использовать Guice и не хотите объявлять все привязки, вы также можете попробовать этот адаптер:
guice-bridge-jit-injector
Ответ 7
Для меня это работает без AbstractBinder
если я включаю следующие зависимости в мое веб-приложение (работает на Tomcat 8.5, Jersey 2.27):
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey-version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext.cdi</groupId>
<artifactId>jersey-cdi1x</artifactId>
<version>${jersey-version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>${jersey-version}</version>
</dependency>
Он работает с CDI 1.2/CDI 2.0 для меня (используя Weld 2/3 соответственно).