Инъекция зависимости с Джерси 2.0
Начиная с нуля без каких-либо предыдущих знаний о Jersey 1.x, мне трудно понять, как настроить внедрение зависимостей в моем проекте Jersey 2.0.
Я также понимаю, что HK2 доступен в Jersey 2.0, но я не могу найти документы, которые бы помогли с интеграцией в Jersey 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
6 ответов
Вы должны определить 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()
вызов метода.
Сначала просто ответить на комментарий в ответ принимает.
"Что делает связывание? Что если у меня есть интерфейс и реализация?"
Это просто читает 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>
Смотрите также:
- Внедрение параметров пользовательского метода в Джерси
- Как внедрить объект в контекст запроса Джерси?
- Как правильно настроить EntityManager в приложении jersey / hk2?
- Запросить объем инъекции в синглтоны
и для общей информации из документации Джерси
ОБНОВИТЬ
Фабрики
Помимо базовой привязки в принятом ответе, у вас также есть фабрики, на которых вы можете иметь более сложную логику создания, а также иметь доступ к информации о контексте запроса. Например
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
в ваш класс ресурсов.
Выбранный ответ датируется некоторое время назад. Нецелесообразно объявлять каждую привязку в пользовательском связывателе HK2. Я использую Tomcat, и мне просто нужно было добавить одну зависимость. Несмотря на то, что он был разработан для Glassfish, он отлично вписывается в другие контейнеры.
<dependency>
<groupId>org.glassfish.jersey.containers.glassfish</groupId>
<artifactId>jersey-gf-cdi</artifactId>
<version>${jersey.version}</version>
</dependency>
Убедитесь, что ваш контейнер тоже настроен правильно ( см. Документацию).
Поздно, но я надеюсь, что это кому-то поможет.
Я определил свой JAX RS следующим образом:
@Path("/examplepath")
@RequestScoped //this make the diference
public class ExampleResource {
Затем, в моем коде, наконец, я могу ввести:
@Inject
SomeManagedBean bean;
В моем случае SomeManagedBean
является компонентом ApplicationScoped
Надеюсь, это кому-нибудь поможет.
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() {..}
}
Для меня это работает без 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 соответственно).
Если вы предпочитаете использовать Guice и не хотите объявлять все привязки, вы также можете попробовать этот адаптер:
Зависимость, необходимая для спокойного обслуживания трикотажа, а Tomcat - это сервер. где ${jersey.version} - 2.29.1
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>2.0.SP1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>${jersey.version}</version>
</dependency>
Базовый код будет следующим:
@RequestScoped
@Path("test")
public class RESTEndpoint {
@GET
public String getMessage() {