CDI 托管 bean 的构造函数在打开页面时被调用两次 [英] Constructor of CDI managed bean is invoked twice while opening the page

查看:19
本文介绍了CDI 托管 bean 的构造函数在打开页面时被调用两次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to use the ChartBean sample from PrimeFaces. This is the view:

<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>

这是豆子:

@Named
@RequestScoped
public class ChartBean implements Serializable {

    private CartesianChartModel categoryModel;
    private CartesianChartModel linearModel;

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

    // ...
}

当我运行它时,我注意到这个 bean 的构造函数在打开页面时被调用了两次.日志显示如下:

While I run it, I noticed that the constructor of this bean is invoked twice while opening the page. The logs shows the following:

信息:ChartBean 已构建
信息:ChartBean 构造

INFO: ChartBean constructed
INFO: ChartBean constructed

所以 bean 被实例化了两次.这是如何引起的,我该如何避免?我正在与数据库交互以获取一些数据以显示在 UI 中,这样数据就不必要地被提取了两次.

So the bean was instantiated twice. How is this caused and how can I avoid that? I'm interacting with the DB to get some data to display in the UI and this way the data is unnecessarily fetched twice.

推荐答案

第一个创建是容器创建 bean 的作用域代理.作用域代理是一个对象,它扩展了您的 bean,并在其他组件需要您的 bean 时被注入.然而,它的方法不执行真正的逻辑,而是将它们的执行委托给 bean 的正确上下文实例.一个例子将阐明:

The first creation is the container creating a scoped proxy of your bean. The scoped proxy is an object that extends your bean and gets injected whenever some other component needs your bean. It's methods however do not execute the real logic, rather delegate their execution to the correct contectual instance of your bean. An example will clarify:

假设有 2 个请求,R1、R2.ChartBean 必须有 2 个实例,B1 和 B2.假设另一个组件 C 依赖于 ChartBean.C 的相关字段必须在应用程序初始化时注入 ChartBean 的实例,并在执行时调用正确的 bean 实例.但是在应用程序初始化时没有请求,当然也没有 B1、B2.容器有什么作用?它创建范围代理并将其注入到需要它的任何人.然后,每当 ChartBean.method() 被调用时,它会在决定调用哪个 bean 的代理上调用(B1 代表 R1,B2 代表 R2,如果没有请求处于活动状态,则抛出异常,例如从 MessageDrivenBean 调用).

Assume 2 requests, R1, R2. There must be 2 instances of ChartBean, B1 and B2. Say another component, C, depends on ChartBean. The relevant field of C must be injected with an instance of ChartBean at application init time and call the correct bean instance at execution time. BUT at app init time there is no request and certainly no B1, B2. What does the container do? It creates the scoped proxy and injects it to whoever needs it. Then, whenever ChartBean.method() is called, it is called on the proxy who decides which is the correct bean to call (B1 for R1, B2 for R2, throw exception if no request is active, e.g. called from a MessageDrivenBean).

前面说过:DO NOT RUN BUSINESS LOGIC IN CONSTRUCTORS IN JAVA EE COMPONENTS,因为构造函数可能会从系统/容器中调用.改用 @PostConstruct 方法:

Having said the previous: DO NOT RUN BUSINESS LOGIC IN CONSTRUCTORS IN JAVA EE COMPONENTS, because constructors may be called from the system/container. Use a @PostConstruct method instead:

...
public class ChartBean implements Serializable {
    public ChartBean() {
        // only construction logic here
    }
    ...
    @PostConstruct
    void postConstruct() {
        createCategoryModel();
        createLinearModel();
    }
}

<小时>

顺便说一句,您可以通过在构造函数中打印类名来验证构造函数是否是从代理实现调用的:


By the way you can verify that the constructor is called from a proxy implementation by printing the class name in the constructor:

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

第一次被调用时,它会是你自己的其他类.

The first time it gets called, it will be some other class than your own.

这篇关于CDI 托管 bean 的构造函数在打开页面时被调用两次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆