如何在RequestScoped bean中调用@PostConstruct之前在SessionScoped bean中设置参数值

时间:2012-02-18 22:22:33

标签: jsf-2

非常感谢您帮助解决以下问题(JBoss 6.0,Mojarra - 2.2 Snapshot,facelet 1.1和PrimeFaces 3.0.M4:

问题是,在获取值集之前调用请求bean的post构造方法。我们如何确保首先设置会话bean的参数值,然后调用请求bean的post构造方法。

问题#1:点击“下一步”时,这是一个ajax调用  1.调用testRequestBB的“初始化”post构造方法  2.调用testSessionBB的“next”方法来设置值

预期的行为应该是另一种方式,使用ajax调用在会话bean中设置值,然后应该初始化请求bean。

问题#2:请求bean的“初始化”post构造方法被调用两次      - 是因为请求bean从基类扩展(尽管基类中没有post构造方法)。

当显示test.xhtml页面时,可以采取哪些措施来解决获取post构造方法两次的问题?

以下是代码:

test.xhtml

<h:dataTable id="testId" emptyMessage="#{messages.noData}" var="test" value="#{testList}">
....
<f:facet name="footer">
    <h:form id="pgId">                  
        <h:commandLink value="#{messages.next} ">
            <f:ajax listener="#{testSessionBB.next}" />
        </h:commandLink>
            .....
    </h:form>
</f:facet>
</h:dataTable>

TestSessionBB.java

@Named("testSessionBB")
@SessionScoped
public class TestSessionBB implements Serializable
{
    private int testStartRow;
    .....

    public String next() 
    {
        if (this.getTestStartRow() + 5 > 15) // hard coded value for simplicity in this post
        {
            this.setTestStartRow(15);
        } else {
            this.setTestStartRow(this.getTestStartRow() + 5);
        }
        log.debug("next - testStartRow: " + this.getTestStartRow());

        return "";
    }
}

TestRequestBB.java

@Named
@RequestScoped
public class TestRequestBB extends testBase implements Serializable {

    ....

    @PostConstruct
    public void initialize()
    {
        log.debug("Initializing TestRequestBB backing bean ...");

        setTestList(allTests()); // load initial list containing 5 rows of test data

        log.debug("Initializing TestRequestBB backing bean done ...");
    }

    @Produces
    @Named
    public List<Test> getTestList()
    {   
        return super.getTestList();
    }
    ....
}

TestBase.java

public abstract class TestBase implements Serializable {

    ..... (contains all common code shared by other classes extending this base class)

    // does not contain any @PostConstruct method

}

1 个答案:

答案 0 :(得分:5)

@PostConstruct确实无法访问更新的模型值。它们仅在更新模型值阶段之后可用(因此,在调用操作和呈现响应阶段期间)。 @PostConstruct从不打算对更新的模型值执行操作,它旨在对注入的依赖项执行操作,这些依赖项又在必要时进行了新构建,例如@EJB@Inject,{ {1}}。

您想要的是在调用操作阶段调用的方法。你需要 @ManagedProperty的ajax监听器方法中完成工作:

TestSessionBB

@Inject private TestRequestBB testRequestBB; public void next() { // No, no need to return String. // ... testRequestBB.initialize(); } 添加到视图中。这允许您在渲染响应阶段之前的调用操作阶段的最后执行bean方法。把它放在你看来的某个地方:

<f:event type="preRenderView">

不要忘记从方法中删除<f:event type="preRenderView" listener="#{testRequestBB.initialize}" /> 注释。

关于@PostConstruct方法被调用两次的问题,那是因为每个@PostConstruct都会产生一个完全独立的实例。你在同一个类上有两个@Named,一个在类本身,另一个在getter方法本身。我建议删除getter方法中的那个,然后在@Named的视图中替换#{testList}

#{testRequestBB.testList}

如果你真的,真的,需要在<h:dataTable value="#{testRequestBB.testList}" ...> 内抓取请求参数,然后你可以通过ExternalContext#getRequestParameterMap()手动抓取它们:

@PostConstruct

如果您使用的是JSF2 @PostConstruct public void initialize() { String foo = (String) FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("foo"); // ... } 注释而不是CDI @ManagedBean注释,那么您也可以使用@Named代替:

@ManagedProperty

此值将在@ManagedProperty("#{param.foo}") private String foo; 期间提供。在CDI中没有这样的注释,但如果有必要,你可以自己homegrow

另见: