【问题标题】:CDI SessionScoped Bean results in two instances in same sessionCDI SessionScoped Bean 在同一会话中产生两个实例
【发布时间】:2011-01-04 14:49:00
【问题描述】:

我有两个用于同一个会话的 SessionScoped CDI bean 实例。我的印象是 CDI 会为我生成一个实例,但它生成了两个。是我误解了 CDI 的工作原理,还是发现了错误?

这是 bean 代码:

package org.mycompany.myproject.session;

import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.faces.context.FacesContext;
import javax.inject.Named;
import javax.servlet.http.HttpSession;

@Named @SessionScoped public class MyBean implements Serializable {
    private String myField = null;

    public MyBean() {
        System.out.println("MyBean constructor called");

        FacesContext fc = FacesContext.getCurrentInstance();
        HttpSession session = (HttpSession)fc.getExternalContext().getSession(false);
        String sessionId = session.getId();
        System.out.println("Session ID: " + sessionId);
    }

    public String getMyField() {
        return myField;
    }

    public void setMyField(String myField) {
        this.myField = myField;
    }
}

这是 Facelet 代码:

<?xml version='1.0' encoding='UTF-8' ?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core">
<f:view contentType="text/html" encoding="UTF-8">
    <h:head>
        <title>Test</title>
    </h:head>
    <h:body>
        <h:form id="form">
            <h:inputText value="#{myBean.myField}"/>
            <h:commandButton value="Submit"/>
        </h:form>
    </h:body>
</f:view>
</html>

这是部署和导航到页面的输出:

INFO: Loading application org.mycompany_myproject_war_1.0-SNAPSHOT at /myproject
INFO: org.mycompany_myproject_war_1.0-SNAPSHOT was successfully deployed in 8,237 milliseconds.
INFO: MyBean constructor called
INFO: Session ID: 175355b0e10fe1d0778238bf4634
INFO: MyBean constructor called
INFO: Session ID: 175355b0e10fe1d0778238bf4634

使用 GlassFish 3.0.1

【问题讨论】:

  • 我实际上被一个相关的问题警告过:在构造函数(或初始化程序块)中调用非最终方法会导致 CDI 出现意外影响。我从那以后读到不建议使用非最终方法(download.oracle.com/javase/tutorial/java/javaOO/initial.html)。如果我使用非最终方法来初始化 CDI bean 中的列表,那么初始化器会被调用两次!注意:CDI 不允许使用 final 方法,并且会抛出运行时异常,说明 bean 不可代理。 “修复”是不调用非最终方法并在初始化程序块中完成所有工作。
  • 我注意到,如果我定义了一个用 @PostConstruct 注释的 init 方法,它只会被调用一次(尽管创建了两个 bean 实例)。我猜 CDI 正在创建我的 bean 实例池,并在将它们从池中拉出时调用 post 构造。我猜想将仍在池中的 bean 实例与当前 HTTP 会话相关联是没有意义的。
  • 在下面进一步查看我的回复。 2 个实例第一个是上下文实例,第二个是代理。 @PostConstruct 当然只会被上下文实例调用,not 被代理调用。

标签: jsf cdi


【解决方案1】:

Ryan,正如 covener 已经写的那样,构造函数也将被调用为该 bean 的每个代理。这是所有代理机制的标准行为,它不仅提供接口代理(如 java.lang.reflect.proxy 的东西),而且提供真正的类代理。

还可以想象每次序列化都会调用 ct。所以如果你在一个负载均衡的集群上工作,你会看到很多次。所以请在一般情况下使用 @PostConstruct 来处理 bean。

LieGrue, 串

【讨论】:

  • 感谢您的回复。不过,我仍然不理解你们中的任何一个。我知道准备 bean 以供使用的替代方法,例如 PostConstruct 生命周期回调和 preRenderView 系统事件;那太好了。我的问题是为什么在一个非常简单的测试中创建会话范围 bean 的两个实例,而只有一个用户访问服务器。我不是在谈论具有负载平衡和集群的重负载服务器。是否创建了多个代理?如果是这样,为什么?或者也许每个代理都由 bean 的两个实例支持?如果有,为什么?
  • Ryan,我知道所有代理的东西都不是那么容易,但是获得 2 次构造函数调用就很好了。第一个实例是上下文实例本身。这是您将存储在 SessionContext 中的 bean。第二个实例是代理。如果您仔细查看调试器或打印出构造函数中的类,那么您会发现这实际上是您的 bean 的子类!
  • 一个是代理,一个是实际的bean。知道了。似乎您在使用 CDI 时必须小心在构造函数中放入的内容。
  • 不仅在 CDI 中 - 在 Spring、EJB、JPA 等中也是如此。基本上在所有以任何形式进行某种代理、序列化和实例管理的框架中。
【解决方案2】:

您的 CDI 实现很可能在新建代理以用于注入点时调用底层 bean 默认构造函数——这是用于焊接和 openwebbeans 的 javassist 的默认行为。

避免在默认构造函数中进行繁重的工作,如果可以,将其移至@PostConstruct!

【讨论】:

  • 好的,但是为什么要创建两个呢? CDI 是否创建池?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-04
  • 1970-01-01
  • 2013-11-26
  • 2013-06-30
  • 2019-06-04
相关资源
最近更新 更多