访问无状态内的现有实例有状态,java ee 6

时间:2012-02-21 20:05:09

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

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

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

我这样想:

Ejb Side:

@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安全因为我不知道如何在RCP应用程序中登录
  4. 我的主要问题是:如何在EJB中访问会话bean(客户端拥有的会话bean)..是否可能?怎么样?
  5. 我问这个家伙几乎要问的是:Concept for reusable login session in rmi ejb calls

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

    提前谢谢

4 个答案:

答案 0 :(得分:10)

你应该从不@Stateful bean(SLSB)中注入@Stateless 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,因此这个答案应该易于理解。

答案 1 :(得分:3)

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

但是你所尝试的是无法奏效的。原因在于,依赖注射(@EJB )和lookup(ctx.lookup ...)保证调用newInstance,因此你将有新的实例。

这在说明书中用以下词语解释:

  

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

答案 2 :(得分:2)

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

我同意它可能令人困惑,但EJB会话bean中唯一的会话保存在您从InititalContext获取的代理bean中。

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

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

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

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

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

我的建议:让你现在的'设计'去吧。不要试图修复它,不要试图证明它。放手吧,删除你的代码,不要回头看。阅读一些关于EJB的好书,然后重新开始。

祝你好运!

答案 3 :(得分:1)

我没有证实你在使用SFSB&amp ;;时是否正确/错误。 SLSB。但下面是你的问题的答案

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

选项-1的问题:您将持久性机制(SFSB)与您的UI代码绑在一起(因为您将它存储在HttpSession中并传递它)。明天,如果要更改为缓存等其他持久性机制,则需要进行大量的返工。同样,如果要将SLSB方法公开为WebService,则无法执行此操作,因为您无法对SFSB对象引用进行xml-ize。简而言之,一个糟糕的选择。

选项-2:在业务层的某个类中有一个静态哈希映射。假设您为每个事务都有一些唯一的事务属性,例如ID。当您启动事务时,从业务层创建SFSB,将其引用存储在静态哈希映射中,并将ID作为键。当您调用SLSB服务方法时,请传递此ID(我假设每个事务都可以具有唯一ID)。从SLSB方法,使用ID查找存储在静态hashmap中的SFSB引用。用它来存储。在此选项中,您的用户界面和SFSB没有耦合。明天,如果您想要了解持久性机制,那么您可以不影响客户端,因为您的更改将受限于BC层。