打开页面时CDI管理的Bean的构造函数被调用两次

3

我正在尝试使用PrimeFaces的ChartBean示例。这是视图:

<h:form>
    <p:layoutUnit position="center">             
    <p:lineChart id="linear" value="#{chartBean.linearModel}" legendPosition="e"
        title="Linear Chart" minY="0" maxY="1000" style="height:600px"/>                        
    </p:layoutUnit>
</h:form>

这是一个bean:
@Named
@RequestScoped
public class ChartBean implements Serializable {

    private CartesianChartModel categoryModel;
    private CartesianChartModel linearModel;

    public ChartBean() {
        System.out.println("ChartBean constructed");
        createCategoryModel();
        createLinearModel();
    }

    // ...
}

当我运行它时,我注意到打开页面时这个bean的构造函数被调用了两次。日志显示如下:
INFO: ChartBean constructed INFO: ChartBean constructed
因此,该bean被实例化了两次。这是怎么引起的,我该如何避免?我正在与数据库交互以获取一些要在UI中显示的数据,这种方式会导致数据不必要地被获取两次。
1个回答

12
第一个创建的是容器创建您的bean的作用域代理。作用域代理是一个扩展您的bean的对象,每当其他组件需要您的bean时,它就会被注入。但是它的方法不会执行实际逻辑,而是将它们的执行委托给您的bean的正确上下文实例。以下是一个示例:
假设有2个请求,R1和R2。必须有2个ChartBean实例B1和B2。假设另一个组件C依赖于ChartBean。在应用程序初始化时,C的相关字段必须使用ChartBean的实例进行注入,并在执行时调用正确的bean实例。但是,在应用程序初始化时没有请求,当然也没有B1、B2。容器会怎么做?它创建了作用域代理,并将其注入到任何需要它的地方。然后,每当调用ChartBean.method()时,它都会调用代理来决定要调用哪个正确的bean(对于R1为B1,对于R2为B2,如果没有活动请求,则抛出异常,例如从MessageDrivenBean中调用)。
基于以上内容,千万不要在Java EE组件的构造函数中运行业务逻辑,因为构造函数可能会从系统/容器中调用。而是应该使用@PostConstruct方法:
...
public class ChartBean implements Serializable {
    public ChartBean() {
        // only construction logic here
    }
    ...
    @PostConstruct
    void postConstruct() {
        createCategoryModel();
        createLinearModel();
    }
}

顺便提一下,你可以通过在构造函数中打印类名来验证它是否是由代理实现调用的:

    public ChartBean() {
        System.out.println("ChartBean as " + this.getClass().getName());
    }

第一次调用它时,将会是一个不同于你自己类的类。

ChartBean作为ec.europa.eu.sep.statisticsep.ChartBean的代理_$$_WeldClientProxy。INFO:ChartBean作为ec.europa.eu.sep.statisticsep.ChartBean。 - Cris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接