在每个请求中调用 ConversationScoped bean 的 PostConstruct 方法

ConversationScoped bean's PostConstruct method called in every request

好吧,我正在使用 ConversationScoped,我希望在对话开始时只调用一次 PostConstruct,请参阅:

@Named("disciplinaDetalheMB")
@ConversationScoped
public class DisciplinaDetalheMBImpl {

    private static final long serialVersionUID = 1L;

    @Inject
        private Conversation conversation;

    @Inject
    @AnBasicBO
    private BasicBO boPadrao;

@PostConstruct
    public void postConstruct() {
    logger.debug("Iniciando PostConstruct...");
    init();
    beginConversation();
    }

public String salvarAndRedirecionar() {
    salvar();
    if (!FacesContext.getCurrentInstance().isValidationFailed()) {
        return goToLastPage() + "?faces-redirect=true";
    } else {
        return "";
    }
    }


private void beginConversation() {
    if (!conversation.isTransient()) {
        endConversation();
    }
    conversation.begin();
    if (conversation.isTransient()) {
        throw new RuntimeException("A conversão não foi iniciada corretamente");
    }
    SessionContext.getInstance().setAttribute("cid", conversation.getId());

    }

    public BasicBO getBoPadrao() {
        return boPadrao;
    }

    public void setBoPadrao(BasicBO boPadrao) {
        this.boPadrao = boPadrao;
    }

}

因此,当创建我的支持 bean 时,对话被初始化,CID 存储在会话中以供之后使用。我的 XHTML 中有一个 commandButton "save",当调用此按钮时,再次调用 PostConstruct 我不知道为什么:

<h:commandLink
            action="#{managedBeanName.salvarAndRedirecionar()}"
            styleClass="btn btn-info pull-right" value="Salvar">
            <f:ajax execute="@form" />
        </h:commandLink>

我注意到生成的 HTML 是:

<a id="formManterDisciplina:j_idt44:j_idt46" href="#" onclick="mojarra.ab(this,event,'action','@form',0);return false" class="btn btn-info pull-right" name="formManterDisciplina:j_idt44:j_idt46">Salvar</a>

所以,我理解 "href=#" 避免执行 onlick。我认为这是问题所在,但我不知道如何解决。提醒:永远不会调用 salvarAndRedirecionar() 方法,因为之前总是调用 postConstruct。

2) 我还有一个问题:如果我开始对话但没有结束,是不是有什么问题?有时我不想手动结束对话,因为我只有一页,我刚开始。

您遇到此问题的原因是您在对话作用域 bean 的 postconstruct 方法中调用了对话开始方法。因此,对话将在呈现响应阶段期间设置为 long-运行 状态,而不是在此之前。问题是 CID 参数是在 HTML 表单元素上呈现的,但此时会话仍处于过渡状态,因为请求后仍未调用 postconstruct 方法。重载commandLink元素时调用postconstruct方法,来不及了,HTML表单元素不会携带CID参数:

<form id="yourForm" name="yourForm" method="post" action="/path/to/yourPage.xhtml" enctype="application/x-www-form-urlencoded">

因此,解决方案包括将对话开始移动到呈现响应阶段之前的某个点。如果您使用的是 JSF 2.2,则可以使用 f:viewAction 标记;如果您使用的是旧版本,则可以使用 f:event 标记。

然后您将看到在 HTML 表单元素中呈现的 CID 参数,如下所示:

<form id="yourForm" name="yourForm" method="post" action="/path/to/yourPage.xhtml?cid=1" enctype="application/x-www-form-urlencoded">

  • 如果您使用 f:event 标签:

在您的页面中:

<f:metadata>
    <f:event listener="#{disciplinaDetalheMB.initConversation}" type="preRenderView" /> 
</f:metadata>

在你的支持 bean 中:

public void initConversation(){
    if (!FacesContext.getCurrentInstance().isPostback() && conversation.isTransient()) {
          conversation.begin();
    }
}
  • 如果您使用 f:viewAction 标签:

在您的页面中:

<f:metadata>
    <f:viewAction action="#{disciplinaDetalheMB.initConversation}" />
</f:metadata>

在你的支持 bean 中:

public void initConversation(){
    if (conversation.isTransient()) {
          conversation.begin();
    }
}

关于你的第二个问题,不结束对话没有什么大问题,因为它像 HTTP 会话一样有超时。您可以根据您的服务器资源管理策略和空闲会话所需的生命周期来设置超时值。无论如何,当你只有一个页面时,你最好使用视图范围的辅助 bean。