【问题标题】:Accessing a SessionScoped bean from another SessionScoped bean more than once in same session creates a new instance在同一会话中多次从另一个 SessionScoped bean 访问 SessionScoped bean 会创建一个新实例
【发布时间】:2014-08-12 14:07:44
【问题描述】:

我在我的应用程序中遇到了我不理解的行为。我有一个 servlet (Servlet1),它接受一个请求,使当前会话无效,创建一个新会话,然后将请求重定向到引用 @Named @SessionScoped bean (豆1)。 servlet 和 bean 都 @Inject 另一个 @SessionScoped bean (SharedBean)。我第一次访问我的 servlet URL 时一切正常。但是,我第二次访问 URL 时,@Named @SessionScoped bean 正在创建一个新的 sharedbean 实例。

所以我希望发生的事情是这样的:

  1. Servlet1 创建一个新会话
  2. Servlet1 初始化 SharedBean
  3. Bean1 访问 SharedBean

这在我第一次访问我的 servlet URL 时正确发生,但在第二次调用时发生的是:

  1. Servlet1 创建一个新会话
  2. Servlet1 初始化 SharedBean
  3. Bean1 创建一个新的 SharedBean

这对我来说似乎不是正确的行为。我知道我在这里遗漏了一些东西,如果有人向我解释,我将不胜感激。代码如下:

Servlet1:

@WebServlet
public class Servlet1 extends HttpServlet
{
    @Inject
    private SharedBean sbean;

    public doGet(HttpServeltRequest request, HttpServletResponse response)
    {
        HttpSession session = request.getSession();
        session.invalidate();
        session             = request.getSession(true);

        this.sbean.initialize();

        response.sendRedirect(newURL);
    }
}

豆1:

@Named
@SessionScoped
public class Bean1 implements Serializable
{
    @Inject
    private SharedBean sbean;

    public void actionMethod()
    {
        this.sbean.execute(); // New instance being created here on 2nd access!
    }
}

SharedBean:

@SessionScoped
public class SharedBean implements Serializable
{
    public void initialize() { /* do some work */ }
    public void exeucte() { /* do some work */ }
}

【问题讨论】:

    标签: session servlets cdi


    【解决方案1】:

    因为每次请求 servlet 时都会使会话无效。所以容器会创建一个新的 bean 或提供另一个实例。在用户或某些其他端点不活动一段时间后,您必须使会话无效。

    SessionScoped 表示 bean 实例对多个请求有效。 但是您必须决定会话对您的应用程序意味着什么。 在大多数情况下,会话与用户登录和注销的时间相关联。所以它由多个请求组成。 所以一个会话对多个请求都是有效的,对不同的 servlet 也是一样的。 会话由 servlet 容器(例如 Tomcat)管理,会话对象的实例通过 ServletContext 实例提供。

    每次请求您的 servlet 时,您都会创建一个新会话。这意味着容器只会为这个单一请求创建一个 ServletContext,并且总是将您的 SharedBean 的新 bean 实例绑定到该上下文实例。

    由于评论而更新。

    在这种情况下,我不建议让容器管理 bean 注入。 因为您永远不知道 bean 实例是在什么时间创建并与之关联的 一个 SharedBean 实例。我认为原因是您在 servlet 请求方法中创建新会话的问题。在这种情况下,SharedBean 的 bean 实例不能相同,因为容器会创建一个新的 SharedBean 实例,并使绑定到 servlet 的共享 bean 实例无效。

    在这种情况下创建 SharedBean 会更好 在 servlet 中,将其作为参数传递给会话上下文并使用 SessionContext 在 bean 中获取 SharedBean 实例。

    由于评论而更新

    注入的 bean 由容器管理。表示容器负责创建和销毁 bean。如果您创建 bean 的新实例,则引用(地址)或 bean 实例与 servlet 容器分离,不再由容器管理。所以共享 bean 引用不适用于您的托管 bean Bean1

    由于评论而更新

    问题的答案是,您在 Servlet 方法的 doGet 方法中创建的 SharedBean 实例未与 servlet 容器通信,因此不会可用于托管 bean Bean1

    【讨论】:

    • 也许我没有很好地解释我的问题。我的尝试是让我的 servlet 每次访问它时都创建一个新会话并初始化我的 SharedBean 然后让 Bean1 访问该 SharedBean 实例。但是 Bean1 正在创建 SharedBean 的新实例。
    • 当你说“你永远不知道 bean 实例是在什么时候创建并与 SharedBean 实例关联的”时,这是我不明白的。 @SessionScoped 的使用不应该在创建 bean 时确切地定义吗?
    • 是的,容器确保共享 bean 的一个 bean 实例绝对可用于会话。但是在您的情况下,您有两个不同的会话。为 servlet 请求创建的一种。第二个是您自己创建的。在 servlet 请求方法中。此时,您无法确定将从容器中为您提供哪个 SharedBean 实例。因此,请尽量避免在您的 servlet doGet 方法中使新会话无效或创建新会话,或自行传递 bean。
    • 我明白你的意思了。在执行 HttpSession.invalidate() 之前和之后,我使用 Bean1 的 toString() 方法对应用程序日志进行了输出,并且值相同。回到绘图板。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-25
    相关资源
    最近更新 更多