4

是否可以在无状态 bean 中访问有状态会话 bean?

我的问题是我有一个名为 User 的会话 bean,我想访问无状态 bean 中的用户信息......

我正在尝试这样:

Ejb端:

@Stateless
public class OfferManagerBean implements OfferManagerLocal, OfferManager
{
    @Resource 
    private SessionContext context;
    @EJB
    private ro.project.ejb.interfaces.User user;
    public String getUsername()
    {
        user = (ro.project.ejb.interfaces.User) context.lookup("java:global/project/projectEJB/User!ro.project.ejb.interfaces.User");
        return user.getUsername();
}

客户端

 User user = (User) ctx.lookup("java:global/project/projectEJB/User!ro.project.ejb.interfaces.User");
 user.setUsername("Alex");

 OfferManager offerManager = (OfferManager) ctx.lookup("java:global/project/projectEJB/OfferManagerBean!ro.project.ejb.interfaces.OfferManager");
 assertEquals(offerManager.getUsername(), "Alex");

这个测试用例的结果是java.lang.AssertionError: expected:<null> but was:<Alex>

它失败了..似乎我请求有状态bean的方式正在向我返回一个新实例...

  1. 我知道为什么这不起作用。因为我的测试失败了:P。我得到一个新实例..
  2. 我想在 EJB 中检查登录用户的某些权限,因为我不想指望客户端,因为我可能会在那里犯错,或者我会告诉其他开发人员为我的项目制作 GUI。
  3. 我不想使用 Java EE Security,因为我不知道如何在 RCP 应用程序中进行登录
  4. 我的主要问题是:如何访问 EJB 中的会话 bean(与客户端相同的).. 可能吗?如何?

我问的几乎和这个人问的一样:Concept for reusable login session in rmi ejb calls

我想这样做,但不是用 JAAS ......

先感谢您

4

4 回答 4

10

永远不应该在bean (SLSB)中注入@Statefulbean (SFSB )。@StatelessSFSB 的生命周期与它的客户端生命周期一样长(客户端是注入 SFSB 的实例,在这种情况下是 SLSB 本身)。然而,SLSB 的意图是无状态的,并且大多数容器都将它们放在一个池中。因此,每当 SLSB 在使用后回到池中时,它将在其他地方完全重用,但它拥有与第一次创建 SLSB 时相同的 SFSB 实例!这可能导致不希望的结果。

此外,每次从 JNDI 获得 SFSB 时,您都会获得一个全新的实例,这与在其他地方共享的 SLSB 不同。SFSB 的客户端就是您从 JNDI 获得 SFSB 的当前客户端类实例。您应该自己保留此实例并重用同一个实例,直到您完成对其执行事务。其中一种方法是将其存储在您自己的 HTTP 会话中或您正在使用的 MVC 框架的会话范围托管 bean 中。

功能要求对我来说并不完全清楚,因此很难给出一个合适的答案来解决您的特定问题,但我的印象是您实际上需要将用户存储在 HTTP 会话中,而不是 SFSB 中。对于会话 bean,初学者最常见的错误是它们错误地将 EJB 上下文中的“会话”解释为 HTTP 会话。

另请参阅有关同类问题的相关答案以获得更深入的解释:JSF request scoped bean keep recreating new Stateful session beans on each request? 根据您的问题历史,您熟悉 JSF,所以这个答案应该很容易理解。

于 2012-02-21T20:29:38.143 回答
3

一般来说,可以在无状态 bean 中访问某些现有的有状态会话 bean。例如,它可以作为无状态会话 bean 的业务方法的参数给出。

但是你正在尝试的东西是行不通的。原因是依赖注入(@EJB)和查找(ctx.lookup...)都保证调用newInstance,因此您将拥有新实例。

这在规范中用以下词语进行了解释:

当客户端通过依赖注入或 JNDI 查找获得对有状态会话 bean 实例的引用时,或者当客户端在会话 bean 的主接口上调用 create 方法时,会话 bean 实例的生命就开始了。这会导致容器在会话 bean 类上调用 newInstance 以创建新的会话 bean 实例。

于 2012-02-21T20:29:58.283 回答
2

如果其他人还不够清楚:你做错了!;)

我同意这可能会造成混淆,但是 EJB 会话 bean 中的唯一会话保存在您从 InitialContext 获得的代理 bean 中。

您从此上下文中获得的不同 bean 不共享任何类型的公共会话。在 EJB 中,bean 不存储在 EJB 会话中,但它们就是这个会话。

换句话说,InitialContext(代码中的 ctx)不是 HttpSession 的 EJB 等价物。

更糟糕的是,在您的代码中,用户是一个 EJB bean。这是错误的。

用户是应用程序中的名词。这些由 JPA 实体或简单的“普通”java bean 表示。EJB 用于在应用程序中实现动词:服务、DAO、存储库等。

有状态会话 bean 中的状态应该在业务流程期间保留模型数据(用于缓存、锁定、保留等目的)。在任何情况下,这种状态都不应成为模型数据。

我的建议:放开你当前的“设计”。不要试图修复它,不要试图证明它的合理性。放手,删除你的代码,不要回头。阅读一些关于 EJB 的好书,然后重新开始。

祝你好运!

于 2012-02-23T09:36:53.803 回答
1

我没有验证您在使用 SFSB 和 SLSB 时是否正确/错误。但以下是您问题的答案

选项 1:从您的 servlet 执行 SFSB 的 JNDI 查找。这应该是一次。将返回的 SFSB 引用存储在您的 HttpSession 中。当您调用需要 SFSB 实例来存储用户详细信息的 SLSB 方法时,将上述 SFSB 引用对象作为参数传递给 SLSB 方法。然后从 SLSB 方法访问它并存储用户数据。

Option-1 的问题:您将持久性机制 (SFSB) 与 UI 代码绑定在一起(因为您将其存储在 HttpSession 中并传递它)。明天,如果你想换成另一种持久化机制,比如缓存,你将需要做大量的返工。同样,如果您想将您的 SLSB 方法公开为 WebService,您将无法这样做,因为您无法对 SFSB 对象引用进行 xml 化处理。简而言之,这是一个糟糕的选择。

选项 2:在业务层的某个类中有一个静态哈希图。假设您对每笔交易都有一些独特的交易属性,例如 ID。当您开始事务时,从您的业务层创建 SFSB,将其引用存储在以 ID 作为键的静态哈希图中。当你调用 SLSB 服务方法时,传递这个 ID(我假设每个事务都可以有一个唯一的 ID)。从 SLSB 方法,使用 ID 查找存储在静态哈希图中的 SFSB 引用。将其用于存储。在此选项中,您的 UI 和 SFSB 不耦合。明天,如果你想改变你的持久性机制,你可以在不影响客户端的情况下做,因为你的更改将被限制在 BC 层内。

于 2012-05-10T15:53:18.167 回答