将EJB注入JAX-RS(RESTful服务)

时间:2010-06-12 07:09:15

标签: java rest java-ee ejb jax-rs

我正在尝试通过注释将无状态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

你们能展示一些有效的例子吗?

7 个答案:

答案 0 :(得分:109)

我不确定这应该有效。所以:

选项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;

    //...
}

请参阅:

答案 1 :(得分:13)

这个线程相当陈旧,不过我昨天也遇到了同样的问题。这是我的解决方案:

只需在课程级别通过@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与ejbs一起打包,则将其放入META-INF。

如果你想使用@EJB,你就完成了。如果你想通过@Inject注入EJB,那么bean.xml也必须放入ejbs jar文件到META-INF中。

您正在做什么:您只是告诉容器资源应该是容器管理的。因此,它支持注入和生命周期事件。因此,您可以拥有自己的业务外观,而无需将其提升为EJB。

您无需为此扩展javax.ws.rs.core.Application。 BookResource是一个自动请求作用域的根资源。

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

快乐的编码。

答案 2 :(得分: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 singleton JAX-RS资源,那么您可以输入 getSingletons

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

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

答案 3 :(得分:5)

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

Zeck,我希望您通过将您的bean推广到EJB来了解您正在做什么,正如Pascal所建议的那样。不幸的是,就像现在使用Java EE“将类创建为EJB”一样简单,您应该意识到这样做的含义。每个EJB都会产生开销以及它提供的附加功能:它们具有事务感知能力,具有自己的上下文,它们参与完整的EJB生命周期等等。

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

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

答案 4 :(得分: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 look up tips

答案 5 :(得分:1)

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

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

避免空指针:

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

它实际上适用于GF

答案 6 :(得分:0)

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