如何将JAX-RS与CDI集成在Servlet 3.0容器中

Seb*_* S. 10 rest jax-rs java-ee cdi servlet-3.0

我有一个使用JSF 2.2(Mojorra 2.1.3)和CDI 1.1(Weld 2.0.3)在Servlet 3.0容器(Jetty 9.0.4)上运行的Web应用程序.没有使用完整的应用程序服务器.在这个应用程序中,我还有一个服务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>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>
...
Run Code Online (Sandbox Code Playgroud)

部署描述符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>
...
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

JAX-RS应用程序配置:

public class RestApplication extends ResourceConfig
{
    public RestApplication()
    {
        // For JSON binding
        register(new JacksonFeature());
        register(new ApplicationBinder());
        packages("my.package.controller");
    }
}
Run Code Online (Sandbox Code Playgroud)

JAX-RS注入绑定配置:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后是JSF托管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";
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,我希望能够在REST资源类和JSF bean中使用相同的应用程序作用域服务实例,但我无法让CDI和JAX-RS协同工作.

JSF/CDI部分工作正常,但注入REST资源类并不真正起作用.我读了一些文章,他们展示了两种不同的方法来组合CDI和JAX-RS:第一种方法是注释REST资源类,@ManagedBean以便类由CDI容器实例化并由JAX管理 - RS容器:

@ManagedBean
@Path("/person")
public class PersonController
{
    @Inject
    private PersonService personService;
    ...
Run Code Online (Sandbox Code Playgroud)

第二种方法是为类提供CDI范围,例如@RequestScoped,以便由CDI容器实例化和管理类.

@Path("/person")
@RequestScoped
public class PersonController
{
    @Inject
    private PersonService personService;
    ...
Run Code Online (Sandbox Code Playgroud)

这些方法都不适合我.我总是得到以下例外:

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)
Run Code Online (Sandbox Code Playgroud)

但是,将注入绑定配置更改为以下时,此错误消失:

bind(PersonServiceImpl.class).to(PersonService.class);
Run Code Online (Sandbox Code Playgroud)

现在注入有些工作,但是对于每个REST请求,我得到一个新的PersonServiceImpl注入实例,即使这个服务是应用程序作用域.对我来说,这是一个指标,JAX-RS组件完全与CDI的东西分开,并且与CDI/JSF的东西一样,生活在一个完全不同的环境或容器中.

所以我真的很想知道如何使这两个概念在纯servlet 3.0容器中协同工作.

Seb*_* S. 12

我解决了我的问题.

问题是,Jersey JAX-RS实现使用HK2依赖注入框架,而这个框架根本不知道CDI bean.并按照在接受答案的想法这个帖子,我使其可用于HK2注射绑定CDI豆类和我的应用程序的注入范围的Bean现在工作得很好.

但我真的很想知道为什么将Java EE的两个组成部分组合在一起这么麻烦.

更新:正如G. Demecki在评论中提到的,这不再需要解决方案了!但在提出这个问题时,它帮助了我.

  • 对不起我迟到的回答.幸运的是,我仍然有一个非常基本的示例项目,除了将服务注入JAX-RS控制器类之外什么都不做.然后通过单元测试调用这些服务,以确保使用相同的注入实例.老实说,我现在还没有处理那些东西.但我想与您分享,希望它能帮助您找出基本部分:http://www.file-upload.net/download-8733326/sample_project.zip.html (2认同)
  • 不,这是不正确的方法,因为使用自定义HK绑定**不再需要**自JEE 7.所有你需要使泽西和CDI的工作是添加依赖于`jersey-gf-cdi-ban -custom-HK2-binding` (2认同)