如何将JAX-RS与CDI集成在Servlet 3.0容器中 [英] How to integrate JAX-RS with CDI in a Servlet 3.0 container
问题描述
我有一个使用JSF 2.2(Mojorra 2.1.3)& ;;在Servlet 3.0容器(Jetty 9.0.4)上运行的Web应用程序。 CDI 1.1(Weld 2.0.3)。没有使用完整的应用程序服务器。在这个应用程序中,我还有一个服务REST请求的JAX-RS 2.0(Jersey 2.2)资源类。我已经集成了JAXB绑定和JSON编组(Jackson 2.2)。我使用Maven 3.0.5进行构建管理。这些是我的项目设置的相关部分:
Maven pom.xml:
...
< dependencies>
<! - Servlet 3.0 API - >
< dependency>
< groupId> javax.servlet< / groupId>
< artifactId> javax.servlet-api< / artifactId>
< version> 3.1.0< / version>
< scope>提供< / scope>
< / dependency>
<! - Java EE的上下文和依赖注入 - >
< dependency>
< groupId> org.jboss.weld.servlet< / groupId>
< artifactId> weld-servlet< / artifactId>
< version> 2.0.3.Final< / version>
< / dependency>
<! - JavaServer Faces - >
< dependency>
< groupId> com.sun.faces< / groupId>
< artifactId> jsf-api< / artifactId>
< version> 2.2.2< / version>
< / dependency>
< dependency>
< groupId> com.sun.faces< / groupId>
< artifactId> jsf-impl< / artifactId>
< version> 2.2.2< / version>
< / dependency>
<! - JAX-RS RESTful Web服务 - >
< dependency>
< groupId> org.glassfish.jersey.containers< / groupId>
< artifactId> jersey-container-servlet< / artifactId>
< version> 2.2< / version>
< / dependency>
<! - JSON Mapping Framework - >
< dependency>
< groupId> com.fasterxml.jackson.core< / groupId>
< artifactId> jackson-databind< / artifactId>
< version> 2.2.2< / version>
< / dependency>
< dependency>
< groupId> org.glassfish.jersey.media< / groupId>
< artifactId> jersey-media-json-jackson< / artifactId>
< version> 2.2< / version>
< / dependency>
< / dependencies>
...
部署描述符web.xml:
...
< context-param>
< param-name> javax.faces.PROJECT_STAGE< / param-name>
< param-value>开发< / param-value>
< / context-param>
< servlet>
< servlet-name> Faces Servlet< / servlet-name>
< servlet-class> javax.faces.webapp.FacesServlet< / servlet-class>
< load-on-startup> 1< / load-on-startup>
< / servlet>
< servlet-mapping>
< servlet-name> Faces Servlet< / servlet-name>
< url-pattern> / jsf / *< / url-pattern>
< / servlet-mapping>
< servlet>
< servlet-name> Jersey REST服务< / servlet-name>
< servlet-class> org.glassfish.jersey.servlet.ServletContainer< / servlet-class>
< init-param>
< param-name> javax.ws.rs.Application< / param-name>
< param-value> my.package.config.RestApplication< / param-value>
< / init-param>
< load-on-startup> 1< / load-on-startup>
< / servlet>
< servlet-mapping>
< servlet-name> Jersey REST服务< / servlet-name>
< url-pattern> / rest / *< / url-pattern>
< / servlet-mapping>
< session-config>
< session-timeout> 30< / session-timeout>
< / session-config>
< listener>
< listener-class> org.jboss.weld.environment.servlet.Listener< / listener-class>
< / listener>
< listener>
< listener-class> com.sun.faces.config.ConfigureListener< / listener-class>
< / listener>
< resource-env-ref>
< description> CDI Bean Manager的对象工厂< / description>
< resource-env-ref-name> BeanManager< / resource-env-ref-name>
< resource-env-ref-type> javax.enterprise.inject.spi.BeanManager< / resource-env-ref-type>
< / resource-env-ref>
...
JAX-RS根资源类:
@Path(/ person)
public class PersonController
{
@Inject
private PersonService personService;
@GET
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public List< Person> getAllPersons()
{
return personService.getAll();
}
@GET
@Path(/ {index})
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Person getPerson(@PathParam(index)int index)
{
return personService.get(index);
}
@POST
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public void savePerson(Person person)
{
personService.add(person);
}
}
JAX-RS应用程序配置:
公共类RestApplication扩展ResourceConfig
{
public RestApplication()
{
//对于JSON绑定
寄存器(new JacksonFeature());
寄存器(new ApplicationBinder());
包(my.package.controller);
}
}
JAX-RS注入绑定配置:
public class ApplicationBinder extends AbstractBinder
{
@Override
protected void configure ()
{
//表示类似:将PersonService类型的注入点处的字段绑定到PersonService
bind(PersonService.class).to(PersonService.class)类型的实例;
}
}
最后是JSF托管bean:
@Named
@SessionScoped
公共类PersonBean实现Serializable
{
private static final long serialVersionUID = 1L;
@Inject
private PersonService personService;
private person newPerson = new Person();
public List< Person> getAll()
{
return personService.getAll();
}
public Person getNewPerson()
{
return newPerson;
}
public void setNewPerson(Person newPerson)
{
this.newPerson = newPerson;
}
public性别[] getGenders()
{
return Gender.values();
}
public String saveNewPerson()
{
personService.add(newPerson);
newPerson = new Person();
返回index;
}
}
最后,我想成为能够在REST资源类和JSF bean中使用相同的应用程序作用域服务实例,但我无法让CDI和JAX-RS协同工作。
JSF / CDI部分工作正常,但注入REST资源类并不真正起作用。我阅读了一些文章,其中他们展示了两种不同的方法来组合CDI和JAX-RS:第一种方法是使用 @ManagedBean
注释REST资源类以便为类由CDI容器实例化并由JAX-RS容器管理:
@ ManagedBean
@Path(/ person)
public class PersonController
{
@Inject
private PersonService personService;
...
第二种方法是给班级一个CDI范围,例如: @RequestScoped
,以便由CDI容器实例化和管理类。
@Path(/ person)
@RequestScoped
public class PersonController
{
@Inject
private PersonService personService ;
...
这些方法都不适合我。我总是遇到以下异常:
org.glassfish.hk2.api.UnsatisfiedDependencyException:在Injectee中没有可用于注射的对象(requiredType = PersonService,parent = PersonController,qualifiers = {}),position = -1,optional = false,self = false,unqualified = null,5643079)
at org。 jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74)
at org.jvnet.hk2.internal.ClazzCreator.resolve(ClazzCreator.java:208)
at org.jvnet.hk2。 internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:231)
at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:328)
at org.jvnet.hk2.internal.SystemDescriptor。创建(SystemDescriptor.java:454)
org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:158)
at org.jvnet.hk2.internal.Utilities.createService( Utilities.java:2296)
at org.jvnet.hk2.internal.Servic eLocatorImpl.getService(ServiceLocatorImpl.java:590)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:577)
at org.glassfish.jersey.internal.inject.Injections。 getOrCreate(Injections.java:172)
但是当将注入绑定配置更改为时,此错误消失: / p>
bind(PersonServiceImpl.class).to(PersonService.class);
现在注入有些工作,但对于每个REST请求,我得到<$ c $的新实例c> PersonServiceImpl 注入,即使此服务是应用程序作用域。对我来说,这是一个指标,JAX-RS组件与CDI完全分离,并且与CDI / JSF一样存在于完全不同的环境或容器中。
<那么我真的很想知道如何让这两个概念在纯servlet 3.0容器中协同工作。
我解决了我的问题。
问题是,Jersey JAX-RS实现使用HK2依赖注入框架,而这个框架根本不知道CDI bean。并按照这篇文章,我让CDI bean可用于HK2注入绑定,并且注入我的应用程序范围的bean现在工作正常。
但我真的很想知道为什么将Java EE的两个组成部分组合在一起这么麻烦。
更新:作为G. Demecki在评论中提到,这是解决方案不再需要!但在提出这个问题时它帮助了我。
I have a web application running on a Servlet 3.0 container (Jetty 9.0.4) using JSF 2.2 (Mojorra 2.1.3) & CDI 1.1 (Weld 2.0.3). No full-fledged application server is used. In this application I also have a JAX-RS 2.0 (Jersey 2.2) resource class serving REST requests. I have integrated JAXB binding and also JSON marshalling (Jackson 2.2). I use Maven 3.0.5 for the build management. These are the relevant parts of my project setup:
Maven pom.xml:
...
<dependencies>
<!-- Servlet 3.0 API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Contexts and Dependency Injection for Java EE -->
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet</artifactId>
<version>2.0.3.Final</version>
</dependency>
<!-- JavaServer Faces -->
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.2.2</version>
</dependency>
<!-- JAX-RS RESTful Web Services -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.2</version>
</dependency>
<!-- JSON Mapping Framework -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
...
Deployment Descriptor web.xml:
...
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/jsf/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>my.package.config.RestApplication</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<listener>
<listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
</listener>
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
<resource-env-ref>
<description>Object factory for the CDI Bean Manager</description>
<resource-env-ref-name>BeanManager</resource-env-ref-name>
<resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
</resource-env-ref>
...
JAX-RS root resource class:
@Path("/person")
public class PersonController
{
@Inject
private PersonService personService;
@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public List<Person> getAllPersons()
{
return personService.getAll();
}
@GET
@Path("/{index}")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Person getPerson(@PathParam("index") int index)
{
return personService.get(index);
}
@POST
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public void savePerson(Person person)
{
personService.add(person);
}
}
JAX-RS application configuration:
public class RestApplication extends ResourceConfig
{
public RestApplication()
{
// For JSON binding
register(new JacksonFeature());
register(new ApplicationBinder());
packages("my.package.controller");
}
}
JAX-RS injection binding configuration:
public class ApplicationBinder extends AbstractBinder
{
@Override
protected void configure()
{
// Means something like: bind the field at an injection point of type PersonService to an instance of type PersonService
bind(PersonService.class).to(PersonService.class);
}
}
And finally the JSF managed bean:
@Named
@SessionScoped
public class PersonBean implements Serializable
{
private static final long serialVersionUID = 1L;
@Inject
private PersonService personService;
private Person newPerson = new Person();
public List<Person> getAll()
{
return personService.getAll();
}
public Person getNewPerson()
{
return newPerson;
}
public void setNewPerson(Person newPerson)
{
this.newPerson = newPerson;
}
public Gender[] getGenders()
{
return Gender.values();
}
public String saveNewPerson()
{
personService.add(newPerson);
newPerson = new Person();
return "index";
}
}
At the end, I want to be able to use the same application scoped service instances in the REST resource classes as well as in the JSF beans, but I can't get CDI and JAX-RS work together.
The JSF/CDI part works fine, but the injection into the REST resource classes does not really work. I read some articles, where they showed two different approaches to combine CDI and JAX-RS: The first one is to annotate the REST resource class with @ManagedBean
in order for the class to be instantiated by the CDI container and to be managed by the JAX-RS container:
@ManagedBean
@Path("/person")
public class PersonController
{
@Inject
private PersonService personService;
...
The second approach is to give the class a CDI scope, e.g. @RequestScoped
in order for the class to be instantiated and managed by the CDI container.
@Path("/person")
@RequestScoped
public class PersonController
{
@Inject
private PersonService personService;
...
None of the approaches work for me. I always end up with the following exception:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=PersonService,parent=PersonController,qualifiers={}),position=-1,optional=false,self=false,unqualified=null,5643079)
at org.jvnet.hk2.internal.ThreeThirtyResolver.resolve(ThreeThirtyResolver.java:74)
at org.jvnet.hk2.internal.ClazzCreator.resolve(ClazzCreator.java:208)
at org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:231)
at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:328)
at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:454)
at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:158)
at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2296)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:590)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:577)
at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:172)
But this error disappears when changing the injection binding configuration to:
bind(PersonServiceImpl.class).to(PersonService.class);
Now injection somehow works, but for every REST request I get a new instance of the PersonServiceImpl
injected, even if this service is application scoped. To me this is an indicator, that the JAX-RS component is totally separated from the CDI stuff and lives in a completely different environment or container as the CDI / JSF stuff does.
So I really wonder how to make these two concepts work together in a pure servlet 3.0 container.
I solved my problem.
The problem is, that Jersey JAX-RS implementation uses the HK2 dependency injection framework and this framework is simply not aware of the CDI beans. And by following the idea of the accepted answer in this post, I make the CDI beans available for the HK2 injection bindings and the injection of my application scoped beans works fine now.
But I really wonder why it is so cumbersome to bring together two constituent parts of Java EE.
Update: As G. Demecki mentioned in a comment, this is solution is no longer needed! But it helped me out at the time of asking this question.
这篇关于如何将JAX-RS与CDI集成在Servlet 3.0容器中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!