在 JAX-RS (RESTful 服务)中注入 EJB

我试图通过注释将 无状态 EJB注入到我的 JAX-RS Web 服务中。不幸的是,EJB 只是 null,当我尝试使用它时,我得到的是 NullPointerException

@Path("book")
public class BookResource {


@EJB
private BookEJB bookEJB;


public BookResource() {
}


@GET
@Produces("application/xml")
@Path("/{bookId}")
public Book getBookById(@PathParam("bookId") Integer id)
{
return bookEJB.findById(id);
}
}

我做错了什么?

下面是一些关于我的机器的信息:

  • 玻璃鱼3.1
  • Netbeans 6.9 RC 2
  • JavaEE6

你们能举个例子吗?

82573 次浏览

我不确定这是否有效,所以要么:

选项1: 使用注入提供程序 SPI

实现一个将执行查找并注入 EJB 的提供程序。参见:

Jersey-server: 1.17:

import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;


import javax.ejb.EJB;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.ws.rs.ext.Provider;
import java.lang.reflect.Type;


/**
* JAX-RS EJB Injection provider.
*/
@Provider
public class EJBProvider implements InjectableProvider<EJB, Type> {


public ComponentScope getScope() {
return ComponentScope.Singleton;
}


public Injectable getInjectable(ComponentContext cc, EJB ejb, Type t) {
if (!(t instanceof Class)) return null;


try {
Class c = (Class)t;
Context ic = new InitialContext();


final Object o = ic.lookup(c.getName());


return new Injectable<Object>() {
public Object getValue() {
return o;
}
};
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

选项2: 使图书资源成为 EJB

@Stateless
@Path("book")
public class BookResource {


@EJB
private BookEJB bookEJB;


//...
}

参见:

选项3: 使用 CDI

@Path("book")
@RequestScoped
public class BookResource {


@Inject
private BookEJB bookEJB;


//...
}

参见:

不幸的是,我的回答对于评论来说太长了,所以就这样吧。 :)

Zeck,我希望您能够意识到,按照 Pascal 的建议,通过将 bean 升级到 EJB,您实际上是在做什么。不幸的是,尽管现在使用 JavaEE 很容易“使一个类成为 EJB”,但是您应该意识到这样做的含义。每个 EJB 都会带来额外的开销以及它所提供的附加功能: 它们支持事务、有自己的上下文、参与整个 EJB 生命周期等等。

我认为对于一个干净和可重用的方法,您应该做的是: 将对服务器服务的访问(希望通过 SessionFacade: 访问)提取到 业务代表中。该委托应该使用某种类型的 JNDI 查找(可能是 服务定位器-是的,它们在 JavaEE 中仍然有效!)进入你的后端。

好的,非正式的: 如果您真的,真的,真的需要注入,因为您不想手动编写 JNDI 访问,您仍然可以使您的委托成为 EJB,尽管它... ... 好吧,它只是感觉不对。:) 这样,如果您决定切换到 JNDI 查找方法,至少以后可以很容易地用其他方法替换它..。

您应该能够在 JAX-RS 资源中进行注入,而不需要使其成为 EJB 或 CDI 组件。但是您必须记住您的 JAX-RS 资源不能是单例的。

因此,您使用这段代码设置应用程序,这使得 图书资源每个要求 JAX-RS 资源。

@javax.ws.rs.ApplicationPath("application")
public class InjectionApplication extends javax.ws.rs.core.Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> classes = new HashSet<Class<?>>();


public InjectionApplication() {
// no instance is created, just class is listed
classes.add(BookResource.class);
}


@Override
public Set<Class<?>> getClasses() {
return classes;
}


@Override
public Set<Object> getSingletons() {
return singletons;
}
}

通过这种设置,您可以让 JAX-RS 在每个请求的基础上为您实例化 图书资源,并注入所有需要的依赖项。如果您使用 图书资源单身 JAX-RS 资源,那么您将放入 获得单身人士

public Set<Object> getSingletons() {
singletons.add(new BookResource());
return singletons;
}

然后,您创建了不由 JAX-RS 运行时管理的实例,容器中没有人关心注入任何东西。

我遇到了同样的问题,我通过上下文查找解决了调用 teEJB 的问题(注入是不可能的,我遇到了相同的 NullPointerException 错误)。

这个问题很老了,不过我昨天还在解决同样的问题,这是我的解决办法:

只需在类级别通过 @ javax 注释 ManagedBean使 BookResource 成为一个托管 bean。

为此,您需要使用 beans.xml 启用 CDI:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

如果 BookResource 是 war 文件的一部分,则此文件需要在 WEB-INF 中。如果 BookResource 与 ejbs 一起打包,则将其放入 META-INF。

如果您想使用@EJB,那么您就完成了。如果希望通过@Inject 注入 EJB,那么还必须将 beans.xml 放入到 ejbs jar 文件中的 META-INF 中。

您正在做的事情: 您只是告诉容器资源应该由容器管理。因此它支持注入和生命周期事件。因此,您可以在不将其提升为 EJB 的情况下获得业务 facade。

您不需要扩展 javax.ws.rs.core。

使用 Glassfish 3.1.2和一个 maven 项目进行测试。

编程愉快。

我也想这么做。我正在使用 EJB 3.1,并将我的应用程序部署为带有单独 EJB 项目的 EAR。正如 Java _ Rock 指出的,我使用上下文查找。

@Path("book")
public class BookResource {


@EJB
BookEJB bookEJB;


public BookResource() {
try {
String lookupName = "java:global/my_app/my_ejb_module/BookEJB";
bookEJB = (BookEJB) InitialContext.doLookup(lookupName);
} catch (NamingException e) {
e.printStackTrace();
}
}


@GET
@Produces("application/xml")
@Path("/{bookId}")
public Book getBookById(@PathParam("bookId") Integer id) {
return bookEJB.findById(id);
}
}

有关非常有用的 JNDI 查找提示,请参见下面的链接

JNDI 查找提示

Arjan 是对的。我创建了另一个类来初始化 EJB,而不是为 RS 创建一个 bean

@Singleton
@LocalBean
public class Mediator {
@EJB
DatabaseInterface databaseFacade;

以避免空指针:

@Path("stock")
public class StockResource {
@EJB
DatabaseInterface databaseFacade;
...

在 GF 身上确实管用