73

我正在尝试通过注释将无状态 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
  • 网豆 6.9 RC 2
  • Java EE 6

你们能举一些工作的例子吗?

4

7 回答 7

113

我不确定这是否应该有效。所以要么:

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

实现一个提供者来进行查找并注入 EJB。看:

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;

    //...
}

看:

选项 3:使用 CDI

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

    @Inject
    private BookEJB bookEJB;

    //...
}

看:

于 2010-06-12T15:36:43.843 回答
15

这个线程相当老,但我昨天刚刚解决了同样的问题。这是我的解决方案:

只需在类级别通过@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 项目进行测试。

快乐编码。

于 2012-10-12T08:06:32.173 回答
10

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

因此,您使用此代码设置您的应用程序。这使得BookResource按请求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单例JAX-RS 资源,这是,你把getSingletons

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

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

于 2012-03-07T17:36:21.783 回答
5

不幸的是,我的回答太长了,无法发表评论,所以就到这里吧。:)

Zeck,我希望您知道通过将您的 bean 升级为 EJB 究竟在做什么,正如 Pascal 所建议的那样。不幸的是,就像现在使用 Java EE '使类成为 EJB' 一样容易,您应该意识到这样做的含义。每个 EJB 都会产生开销以及它提供的附加功能:它们是事务感知的、有自己的上下文、它们参与完整的 EJB 生命周期等。

我认为您应该为干净且可重用的方法做的是:将对服务器服务的访问(希望通过SessionFacade访问:) 提取到BusinessDelegate中。该委托应该使用某种 JNDI 查找(可能是ServiceLocator - 是的,它们在 Java EE 中仍然有效!)来访问您的后端。

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

于 2011-09-01T07:11:29.543 回答
3

我试图做同样的事情。我正在使用 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 查找提示

于 2015-05-28T18:12:47.467 回答
1

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

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

避免空指针:

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

它实际上适用于GF

于 2015-08-21T08:47:05.320 回答
0

我有同样的问题,我通过上下文查找调用 te EJB 解决了它(注入是不可能的,我有同样的错误 NullPointerException)。

于 2012-07-13T23:18:27.703 回答