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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3027834/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:43:41  来源:igfitidea点击:

Inject an EJB into JAX-RS (RESTful service)

javarestjakarta-eeejbjax-rs

提问by Zeck

I'm trying to inject a Stateless EJBinto my JAX-RS webservice via annotations. Unfortunately the EJB is just nulland I get a NullPointerExceptionwhen I try to use it.

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

What am I doing wrong?

我究竟做错了什么?

Here is some information about my machine:

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

  • Glassfish 3.1
  • Netbeans 6.9 RC 2
  • Java EE 6
  • 玻璃鱼 3.1
  • Netbeans 6.9 RC 2
  • Java EE 6

Can you guys show some working example?

你们能展示一些工作示例吗?

采纳答案by Pascal Thivent

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. See:

实现将执行查找并注入 EJB 的提供程序。看:

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:

看:

回答by LeChe

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. 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. 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.

Zeck,我希望您能按照 Pascal 的建议,通过将 bean 提升到 EJB 来了解您到底在做什么。不幸的是,尽管现在使用 Java EE 很容易“使类成为 EJB”,但您应该意识到这样做的含义。每个 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. 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.

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

Okay, off the record: if you really, really, reallyneed 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. :) 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 访问,你仍然可以让你的委托成为 EJB,尽管它......好吧,感觉是错误的。:) 这样,如果您确实决定切换到 JNDI 查找方法,那么至少以后可以很容易地用其他东西替换它...

回答by Martin

You shall be able to do injection in JAX-RS resource without making it EJB or CDI component. But you have to remember that your JAX-RS resource must not be singleton.

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

So, you setup your application with this code. This makes BookResourceclass per-requestJAX-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 BookResourcefor you on per-request basis and also inject all the required dependencies. If you make BookResourceclass singletonJAX-RS resource, this is, you put in getSingletons

通过此设置,您可以让 JAX-RS根据每个请求为您实例化BookResource,并注入所有必需的依赖项。如果您将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 运行时管理的实例,并且容器中没有人关心注入任何内容。

回答by NIkoas

I have the same problem, and I solved it calling te EJB by a context lookup (the injection was impossible, I had the same error NullPointerException).

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

回答by Michael Simons

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.ManagedBeanat 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. If the BookResource is packaged with the ejbs put it into META-INF.

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

If you want to use @EJB you're done. 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.

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

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. BookResource is as a root resource automatically request scoped.

您无需扩展 javax.ws.rs.core.Application 即可使其工作。BookResource 是作为根资源自动请求范围的。

Tested with Glassfish 3.1.2 and a maven project.

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

Happy coding.

快乐编码。

回答by duvo

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. As Jav_Rock pointed out, I use context lookup.

我试图做完全相同的事情。我正在使用 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);
  }
}

See the link below for very useful JNDI look up tips

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

JNDI look up tips

JNDI 查找提示

回答by Lukasz Ronikier

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