在同一会话中多次从另一个SessionScoped bean访问SessionScoped bean将创建一个新实例

在同一会话中多次从另一个SessionScoped bean访问SessionScoped bean将创建一个新实例,session,servlets,cdi,Session,Servlets,Cdi,我在我的应用程序中遇到了我不理解的行为。我有一个servlet(Servlet1),它接受一个请求,使当前会话无效,创建一个新会话,然后在请求上重定向到一个JSF页面,该页面引用一个@Named@SessionScoped bean(Bean1)。servlet和bean@都注入了另一个@SessionScoped bean(SharedBean)。第一次访问servlet URL时,一切正常。然而,第二次访问URL时,@Named@SessionScoped bean正在创建一个新的share

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

所以我希望发生的是:

  • Servlet1创建新会话
  • Servlet1初始化SharedBean
  • Bean1访问SharedBean
  • 这在我第一次访问servlet URL时是正确的,但在第二次调用时发生的是:

  • Servlet1创建新会话
  • Servlet1初始化SharedBean
  • Bean1创建一个新的SharedBean
  • 对我来说,这似乎不是正确的行为。我知道我遗漏了一些东西,如果有人能给我解释一下,我将不胜感激。代码如下:

    服务1:

    @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);
        }
    }
    
    Bean1:

    @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 */ }
    }
    

    因为每次请求servlet时,都会使会话无效。因此,容器创建一个新bean或提供另一个实例。在用户或某个其他端点处于非活动状态一段时间后,必须使会话无效

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

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

    因评论而更新

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

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

    因评论而更新

    注入的bean由容器管理。表示容器负责创建和销毁bean。如果创建bean的新实例,那么引用(地址)或bean实例将与servlet容器解耦,不再由容器管理。因此,共享bean引用不适用于托管beanBean1

    因评论而更新


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

    也许我没有很好地解释我的问题。我的尝试是让我的servlet在每次访问它时创建一个新会话,并初始化我的SharedBean,然后让Bean1访问该SharedBean实例。但是Bean1正在创建一个新的SharedBean实例。当你说“你永远不知道bean实例是在什么时候创建的,并且与SharedBean实例关联”,这是我不理解的。@SessionScoped的使用不应该精确定义bean的创建时间吗?是的,容器确保共享bean的一个bean实例可用于会话。但在你的情况下,你有两个不同的会议。为servlet请求创建的一个。第二次是你自己创造的。在servlet请求方法中。此时,您无法确定将从容器中为您提供哪个SharedBean实例。因此,尽量避免在servlet doGet方法中无效或创建新会话,或者自己传递bean。在执行HttpSession.invalidate()之前和之后,我使用Bean1的toString()方法向应用程序日志中输出了一个值,这些值是相同的。回到绘图板上。