将 EJB 注入 JAX-RS(RESTful 服务)
Posted
技术标签:
【中文标题】将 EJB 注入 JAX-RS(RESTful 服务)【英文标题】:Inject an EJB into JAX-RS (RESTful service) 【发布时间】:2011-03-02 22:52:41 【问题描述】:我正在尝试通过注释将 Stateless 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);
我做错了什么?
以下是关于我的机器的一些信息:
Glassfish 3.1 Netbeans 6.9 RC 2 Java EE 6你们能展示一些工作示例吗?
【问题讨论】:
【参考方案1】:我不确定这是否应该有效。所以要么:
选项 1:使用注入提供程序 SPI
实现一个提供者来进行查找和注入 EJB。见:
@EJB injection。com.sun.jersey: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:使 BookResource 成为 EJB
@Stateless
@Path("book")
public class BookResource
@EJB
private BookEJB bookEJB;
//...
见:
How to Combine REST Services with EJB 3.1 EJB 3.1 And REST - The Lightweight Hybrid选项 3:使用 CDI
@Path("book")
@RequestScoped
public class BookResource
@Inject
private BookEJB bookEJB;
//...
见:
Injecting an EJB from a jar into a jax-rs class in a war【讨论】:
亲爱的@Pascal Thivent 将 JAX-Resources 作为@Stateless
是唯一的出路吗?
@Pascal 选项 3 也可以如下所示:@EJB
private BookEJB bookEJB 并且它可以工作 (GF 3.1.2)
“我不确定这是否可行。” - 为什么?我希望你可以在不玩一堆游戏的情况下注入一个 EJB
@Ryan:不,这一点都不容易,因为 Glassfish 4.1 HK2 存在错误。我今天遇到了同样的问题。
@Pascal Thivent:第二个选项有什么缺点吗?喜欢..它会增加jboss中的bean吗?【参考方案2】:
很遗憾,我的回答太长,无法发表评论,所以就这样吧。 :)
Zeck,我希望您了解通过将 bean 升级为 EJB 所做的具体操作,正如 Pascal 所建议的那样。不幸的是,就像现在使用 Java EE '使一个类成为 EJB' 一样容易,您应该意识到这样做的含义。每个 EJB 都会产生开销以及它提供的附加功能:它们是事务感知的、有自己的上下文、它们参与整个 EJB 生命周期等。
我认为你应该为一个干净和可重用的方法做的是:提取对你的服务器服务的访问(希望通过SessionFacade :) 进入BusinessDelegate。此委托应该使用某种 JNDI 查找(可能是 ServiceLocator - 是的,它们在 Java EE 中仍然有效!)来访问您的后端。
好吧,不记录了:如果你真的,真的,真的需要注入,因为你不想手动编写 JNDI 访问,你仍然可以让你的委托成为 EJB,尽管它.. ……嗯,就是感觉不对。 :) 这样,如果您决定切换到 JNDI 查找方法,至少以后可以很容易地用其他东西替换它......
【讨论】:
Errr,这就是您没有完整阅读问题时得到的结果。对于您的 RESTful 服务,可以说,服务本身就是您的 BusinessDelegate。所以你有它:我声明的第一部分和最后一部分仍然完全有效,而我可能不会为 BusinessDelegate 创建一个 BusinessDelegate ... :) 我也不愿意让我的 api 端点 EJB,但你可以只使用 @ManagedBean。喜欢的话可以看我的回答 您提供的 JNDI 是在Option 1: Use the injection provider SPI
示例中开发的。
人们经常谈论 EJB 的“开销”,好像它们是重量级的已经是公认的事实,但大多数开发人员从来没有费心去衡量它。 Adam Bien 表明 EJB 和 POJO 之间的性能差异在 2008 年 (adam-bien.com/roller/abien/entry/is_it_worth_using_pojos) 很小 (
我认为你误解了 10% 的数字,乐车。这不是“我的应用程序现在慢了 10%”,而是应用程序中的一种特定类型的操作慢了 10%。使用 EJB 代替 POJO 不会使网络、数据库、文件 IO 和渲染花费 10% 以上的时间,而这些操作构成了许多系统的主体。在实际应用程序中使用 EJB 与 POJO 的总体性能影响可能可以忽略不计。至于您评论的后半部分,我同意有充分的理由不将所有内容都设为 EJB:幸运的是,Pascal 提出了两个替代方案。【参考方案3】:
您应该能够在 JAX-RS 资源中进行注入,而无需使其成为 EJB 或 CDI 组件。但是您必须记住,您的 JAX-RS 资源不能是单例的。
因此,您使用此代码设置您的应用程序。这使得 BookResource 类 per-request 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 根据每个请求为您实例化 BookResource 并注入所有必需的依赖项。如果你制作 BookResource 类 singleton JAX-RS 资源,这就是你放入 getSingletons
public Set<Object> getSingletons()
singletons.add(new BookResource());
return singletons;
然后,您创建了不受 JAX-RS 运行时管理的实例,并且容器中没有人关心注入任何东西。
【讨论】:
“单例”与@EJB
问题无关,对吧?
就我而言,我的 REST 资源是单例的。所以即使我注入会话bean(使用@EJB 或@Inject),通过bean 实例总是为空,导致NullPointerException。您关于单身人士的最后提示帮助我解决了这个问题。我确保我的 REST 资源不是单例的。
旧帖子,但非常感谢@Martin!从昨天开始,我一直在努力解决这个问题。您对单例的提示成功了。【参考方案4】:
我有同样的问题,我通过上下文查找调用 te EJB 解决了它(注入是不可能的,我有同样的错误 NullPointerException)。
【讨论】:
【参考方案5】:这个帖子比较老了,不过我昨天也遇到了同样的问题。这是我的解决方案:
只需在类级别通过@javax.annotation.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 与 ejb 打包在一起,则将其放入 META-INF。
如果你想使用@EJB,你就完成了。如果你想通过@Inject 注入 EJB,那么也必须将 beans.xml 放入 ejbs jar 文件到 META-INF 中。
你在做什么:你只是告诉容器资源应该由容器管理。因此它支持注入以及生命周期事件。因此,您无需将其提升为 EJB 即可拥有业务外观。
您无需扩展 javax.ws.rs.core.Application 即可使用。 BookResource 是作为根资源自动请求作用域的。
使用 Glassfish 3.1.2 和 maven 项目进行测试。
编码愉快。
【讨论】:
我知道这个答案非常陈旧,但是:META-INF/beans.xml
与 Java EE @ManagedBean
注释没有任何关系;你不应该需要它。我也使用@ManagedBean
让我的资源类允许其中的@EJB
引用并且不需要META-INF/beans.xml
参与。同样值得注意的是,如果你这样做了,如果你想在部署描述符中覆盖 @EJB
注释,你会感到很麻烦,因为@ManagedBean
s 没有注释。
我遇到了同样的问题。我需要 beans.xml 让它运行。我的环境是带有球衣休息服务的 JBoss EAP 6.4。我遇到了JBAS011859。但最后它运行了。【参考方案6】:
我试图做同样的事情。我正在使用 EJB 3.1 并将我的应用程序部署为具有单独 EJB 项目的 EAR。正如 Jav_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 look up tips
【讨论】:
我确实使用了这种方法并且它确实有效。但是,相同的代码在 jboss 中运行良好...所以我真的不确定为什么会发生这种情况.. 我不得不将它用于旧版应用程序。然而在 jboss 6.4 中,我发现只有完整的 jndi 名称有效:java:global/completeEar/completeEar-ejb-1.0-SNAPSHOT/TestEJBImpl!personal.ejb.TestEJBRemote【参考方案7】:阿扬是对的。我创建了另一个类来初始化 EJB,而不是为 RS 创建一个 bean
@Singleton
@LocalBean
public class Mediator
@EJB
DatabaseInterface databaseFacade;
为了避免空指针:
@Path("stock")
public class StockResource
@EJB
DatabaseInterface databaseFacade;
...
它实际上适用于 GF
【讨论】:
以上是关于将 EJB 注入 JAX-RS(RESTful 服务)的主要内容,如果未能解决你的问题,请参考以下文章
jax-rs RESTful 服务的 spring 安全配置