简体   繁体   English

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

[英]Inject an EJB into JAX-RS (RESTful service)

I'm trying to inject a Stateless EJB into my JAX-RS webservice via annotations. 我正在尝试通过注释将无状态EJB注入我的JAX-RS Web服务。 Unfortunately the EJB is just null and I get a NullPointerException when I try to use it. 不幸的是,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);
    }
}

What am I doing wrong? 我究竟做错了什么?

Here is some information about my machine: 以下是有关我的机器的一些信息:

  • Glassfish 3.1 Glassfish 3.1
  • Netbeans 6.9 RC 2 Netbeans 6.9 RC 2
  • Java EE 6 Java EE 6

Can you guys show some working example? 你们能展示一些有用的例子吗?

I am not sure this is supposed to work. 我不确定这应该有效。 So either: 所以要么:

Option 1: Use the injection provider SPI 选项1:使用注射提供者SPI

Implement a provider that will do the lookup and inject the EJB. 实现将执行查找并注入EJB的提供程序。 See: 看到:

Example for com.sun.jersey:jersey-server:1.17 : 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;
        }
    }
}

Option 2: Make the BookResource an EJB 选项2:使BookResource成为EJB

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

    @EJB
    private BookEJB bookEJB;

    //...
}

See: 看到:

Option 3: Use CDI 选项3:使用CDI

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

    @Inject
    private BookEJB bookEJB;

    //...
}

See: 看到:

This thread is rather old, nevertheless i fought the same problem just yesterday. 这个线程相当陈旧,不过我昨天也遇到了同样的问题。 Here is my solution: 这是我的解决方案:

Just make the BookResource a managed bean through @javax.annotation.ManagedBean at class level. 只需通过类级别的@ javax.annotation.ManagedBean将BookResource设置为托管bean。

For this to work you need to enable CDI with a beans.xml: 为此,您需要使用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>

This file needs to be in WEB-INF if the BookResource is part of a war file. 如果BookResource是war文件的一部分,则此文件需要在WEB-INF中。 If the BookResource is packaged with the ejbs put it into META-INF. 如果BookResource与ejbs一起打包,则将其放入META-INF。

If you want to use @EJB you're done. 如果你想使用@EJB,你就完成了。 If you want to inject the EJB through @Inject than a beans.xml must be put into the ejbs jar file into META-INF as well. 如果要通过@Inject注入EJB,则必须将beans.xml放入ejbs jar文件中,并将其放入META-INF中。

What you're doing: You're just telling the container that the resource should be container managed. 你在做什么:你只是告诉容器资源应该是容器管理的。 Therefor it supports injection as well as lifecycle events. 因此,它支持注入和生命周期事件。 So you have your business facade without promoting it to an EJB. 因此,您拥有业务外观,而无需将其提升为EJB。

You don't need to extend javax.ws.rs.core.Application for this to work. 您无需为此扩展javax.ws.rs.core.Application。 BookResource is as a root resource automatically request scoped. BookResource作为根资源自动请求作用域。

Tested with Glassfish 3.1.2 and a maven project. 使用Glassfish 3.1.2和maven项目进行测试。

Happy coding. 快乐的编码。

You shall be able to do injection in JAX-RS resource without making it EJB or CDI component. 您应该能够在JAX-RS资源中进行注入,而无需使用EJB或CDI组件。 But you have to remember that your JAX-RS resource must not be singleton. 但是你必须记住你的JAX-RS资源不能是单例。

So, you setup your application with this code. 因此,您使用此代码设置应用程序。 This makes BookResource class per-request JAX-RS resource. 这使得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;
  }
}

With this setup, you are letting JAX-RS to instantiate BookResource for you on per-request basis and also inject all the required dependencies. 通过此设置,您可以让JAX-RS根据请求为您实例化BookResource ,并注入所有必需的依赖项。 If you make BookResource class singleton JAX-RS resource, this is, you put in getSingletons 如果您将BookResource类设置为单例 JAX-RS资源,那么就是放入getSingletons

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

then, you created instance which is not managed by JAX-RS runtime and nobody in container cares to inject anything. 然后,您创建了一个不受JAX-RS运行时管理的实例,并且容器中没有人关心注入任何内容。

Unfortunately, my answer is too long for a comment, so here goes. 不幸的是,我的回答太长了,无法发表评论,所以这里有。 :) :)

Zeck, I hope that you are aware of what exactly you are doing by promoting your bean to an EJB, as suggested by Pascal. Zeck,我希望你通过将你的bean推广到EJB来了解你正在做什么,正如Pascal所建议的那样。 Unfortunately, as easy as it is nowadays with Java EE to 'make a class an EJB', you should be aware of the implications of doing so. 不幸的是,就像现在使用Java EE“将类创建为EJB”一样简单,您应该意识到这样做的含义。 Each EJB creates overhead along with the additional functionality it provides: they are transaction aware, have their own contexts, they take part in the full EJB life cycle, etc. 每个EJB都会产生开销以及它提供的附加功能:它们具有事务感知能力,具有自己的上下文,它们参与完整的EJB生命周期等。

What I think you should be doing for a clean and reusable approach is this: extract the access to your servers services (which hopefully are accessed through a SessionFacade :) into a BusinessDelegate . 我认为你应该采用干净和可重用的方法做到这一点:将对服务器服务(希望通过SessionFacade访问)的访问权限提取到BusinessDelegate中 This delegate should be using some kind of JNDI lookup (probably a ServiceLocator - yes, they are still valid in Java EE!) to access your backend. 这个委托应该使用某种JNDI查找(可能是ServiceLocator - 是的,它们在Java EE中仍然有效!)来访问你的后端。

Okay, off the record: if you really, really, really need the injection because you do not want to write JNDI access manually, you could still make your delegate an EJB, although it ... well, it just feels wrong. 好的,关闭记录:如果你真的,真的, 真的需要注入,因为你不想手动编写JNDI访问,你仍然可以让你的委托成为EJB,虽然它......好吧,它只是感觉不对。 :) That way at least it will be easy to replace it later with something else if you do decide to switch to a JNDI lookup approach... :)至少如果您决定切换到JNDI查找方法,至少可以很容易地用其他东西替换它...

I was trying to do the exact same thing. 我试图做同样的事情。 I'm using EJB 3.1 and have a deployed my app as an EAR with separate EJB project. 我正在使用EJB 3.1并将我的应用程序部署为具有单独EJB项目的EAR。 As Jav_Rock pointed out, I use context lookup. 正如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);
  }
}

See the link below for very useful JNDI look up tips 请参阅下面的链接,了解非常有用的JNDI查找提示

JNDI look up tips JNDI查找提示

Arjan is right. 阿尔詹是对的。 I created another class to initialize the EJB instead of creating a bean for RS 我创建了另一个类来初始化EJB,而不是为RS创建一个bean

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

to avoid null pointer with: 避免空指针:

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

it actually works on GF 它实际上适用于GF

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM