【问题标题】:access existing instance stateful inside stateless, java ee 6在无状态、java ee 6 中访问现有的有状态实例
【发布时间】:2012-03-12 03:58:05
【问题描述】:

是否可以在无状态 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...

提前谢谢你

【问题讨论】:

    标签: jakarta-ee ejb-3.0 stateful-session-bean


    【解决方案1】:

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

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

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

    另请参阅有关同类问题的相关答案以获得更深入的解释:JSF request scoped bean keeps recreating new Stateful session beans on every request? 根据您的问题历史,您熟悉 JSF,因此该答案应该易于理解。

    【讨论】:

    • 我知道stateful 是什么意思.. 但是不能访问用户拥有的东西吗?例如.. 如果有一个名为CartBeanstateful bean... 并且我有一个对象列表... 我可以直接在EJB 中检索该列表吗?或者客户端必须再次调用stateless bean 作为位于CartBean 中的列表的参数?我的问题是...我可以直接从 EJB 访问 stateful bean 吗?
    • 鉴于您的设计方法和您对每次 JNDI 调用都返回一个新实例的“惊讶”,您似乎不理解它。至于购物车示例,要么在 SFSB 本身中完成工作,要么在客户端中完成工作,然后将其传递给 SLSB。 SLSB 不应持有任何状态(因此,“无状态”)并且绝对不是 SFSB。 SLSB 将在多个客户端之间汇集和共享。
    • 好的..据我所知...我的stateful bean存在于我的ejb jvm中..我不能访问那部分吗?我有一个连接到 ejb 的独立客户端......我想从那个有状态的 EJB 中获取一些信息......我只能通过参数来做到这一点吗?我不能以其他方式做吗?...我不想使用 java EE 安全性... PS:这对我来说并不奇怪 :) 我只是在询问之前进行测试 :))
    • 每个客户端都有自己的 SFSB 实例,该实例在其他地方共享。我仍然不确定您的具体功能要求是什么,但肯定必须以不同的方式解决。
    • 您实际上没有在问题的任何地方说明具体的功能要求。所以没有人能够回答它。您唯一的问题是“是否可以在无状态 bean 中访问有状态会话 bean?”答案是“否”。您必须自己传递它或根据具体的功能要求寻找替代方法。
    【解决方案2】:

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

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

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

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

    【讨论】:

      【解决方案3】:

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

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

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

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

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

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

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

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

      祝你好运!

      【讨论】:

      • 谢谢.. 我只是在询问之前进行测试 :)) 我只是好奇这是否可能......我读了一些好的 EJB 书籍,但我没有看到这种方法或告诉我我不能这样做...谢谢您的回答...
      【解决方案4】:

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

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

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

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

      【讨论】:

      • 这些方法不好...... :)
      猜你喜欢
      • 2013-02-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-27
      • 2011-07-11
      • 2017-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多