Вопрос-ответ

Dependency injection with Jersey 2.0

Внедрение зависимостей с помощью Jersey 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

Переведено автоматически
Ответ 1

Вам необходимо определить AbstractBinder и зарегистрировать его в вашем приложении JAX-RS. Связующее указывает, как внедрение зависимостей должно создавать ваши классы.

public class MyApplicationBinder extends AbstractBinder {
@Override
protected void configure() {
bind(MyService.class).to(MyService.class);
}
}

Когда @Inject обнаруживается параметр или поле типа MyService.class, его экземпляр создается с использованием класса MyService. Чтобы использовать этот binder, его необходимо зарегистрировать в приложении 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>

Implement the MyApplication class (specified above in the init-param).

public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new MyApplicationBinder());
packages(true, "com.mypackage.rest");
}
}

The binder specifying dependency injection is registered in the constructor of the class, and we also tell the application where to find the REST resources (in your case, MyResource) using the packages() method call.

Ответ 2

First just to answer a comment in the accepts answer.


"What does bind do? What if I have an interface and an implementation?"


It simply reads bind( implementation ).to( contract ). You can alternative chain .in( scope ). Default scope of PerLookup. So if you want a singleton, you can

bind( implementation ).to( contract ).in( Singleton.class );

There's also a RequestScoped available

Also, instead of bind(Class).to(Class), you can also bind(Instance).to(Class), which will be automatically be a singleton.


Adding to the accepted answer

For those trying to figure out how to register your AbstractBinder implementation in your web.xml (i.e. you're not using a ResourceConfig), it seems the binder won't be discovered through package scanning, i.e.

<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>

Or this either

<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
com.foo.YourBinderImpl
</param-value>
</init-param>

To get it to work, I had to implement a 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;
}
}

The @Provider annotation should allow the Feature to be picked up by the package scanning. Or without package scanning, you can explicitly register the Feature in the 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>

See Also:

and for general information from the Jersey documentation


UPDATE

Factories

Aside from the basic binding in the accepted answer, you also have factories, where you can have more complex creation logic, and also have access to request context information. For example

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);
}
});

Then you can inject MyService into your resource class.

Ответ 3

The selected answer dates from a while back. It is not practical to declare every binding in a custom HK2 binder.
I'm using Tomcat and I just had to add one dependency. Even though it was designed for Glassfish it fits perfectly into other containers.

   <dependency>
<groupId>org.glassfish.jersey.containers.glassfish</groupId>
<artifactId>jersey-gf-cdi</artifactId>
<version>${jersey.version}</version>
</dependency>

Make sure your container is properly configured too (see the documentation).

Ответ 4

Late but I hope this helps someone.

I have my JAX RS defined like this:

@Path("/examplepath")
@RequestScoped //this make the diference
public class ExampleResource {

Then, in my code finally I can inject:

@Inject
SomeManagedBean bean;

In my case, the SomeManagedBean is an ApplicationScoped bean.

Hope this helps to anyone.

2023-10-17 13:07 java