【问题标题】:How to set parameter value in SessionScoped bean before @PostConstruct gets called in RequestScoped bean如何在 RequestScoped bean 中调用 @PostConstruct 之前在 SessionScoped bean 中设置参数值
【发布时间】:2012-03-09 19:47:14
【问题描述】:

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

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

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

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

问题 #2:请求 bean 的“初始化”后构造方法被调用了两次。
- 是不是因为请求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

}

【问题讨论】:

  • 几点说明;您没有显示任何使用TestRequestBB 的代码。 Facelet 仅使用TestSessionBB。此外,如果您使用的是 JSF 的 beta 版本(在本例中为 2.2),您应该预料到会出现问题,所以如果有任何奇怪的行为测试,请首先使用已发布的版本。 facelets 1.1 怎么了?您是否将那个 jar 打包到您的应用程序中? JSF 带有 Facelets(在 JSF 2.x 的情况下为 2.0)。不要单独包装。最后,在会话范围的 bean 中实现寻呼机可能不是一个好主意。
  • 当数据表上的“testList”被调用时,它被声明为@Produced方法,它会初始化TestReqestBB。是的,你是对的,我使用的是与 jsf 2.2 捆绑在一起的 facelets,而不是 facelet 1.1。关于 Mojarra 2.2。快照,在早期的稳定版本中也看到了两次调用 PostConstruct 方法的类似行为,因此我使用最新的快照来查看它是否可以消失,但不是那么远。将感谢所有解决此问题的建议。
  • 我也有同样的问题,只是使用了 RequestScoped-bean。我将请求参数传递给该页面/bean。该 bean 是用 CDI 构造的,并有一个 PostConstruct 方法,该方法应该加载属于随请求而来的 ID 的对象。但是该值是在“应用请求值”阶段应用的,并且 postconstruct 被提前调用(在构造函数完成之后)。我在 Mojarra 2.1.2 上。谢谢
  • @lostiniceland:所以对你来说,@PostConstruct 也被调用了两次?
  • @BalusC 没有。只有一次,但在请求值映射到 bean 之前。我读到的关于 PostConstruct-Annotation 的内容是在所有依赖注入完成后直接调用的......但它没有考虑 JSF 生命周期。我想我做错了什么,因为它应该是一个标准程序,但我的书和到目前为止我发现的其他示例都是简单的 helloworld-ones,其中参数来自其他 SessionScoped-Beans(不随请求一起传递) .

标签: jsf-2


【解决方案1】:

@PostConstruct 确实永远无法访问更新的模型值。它们仅在更新模型值阶段之后可用(因此,在调用操作和呈现响应阶段)。 @PostConstruct 从来没有打算对更新的模型值执行操作,它的目的是对注入的依赖项执行操作,如果需要,这些依赖项又是新构建的,例如 @EJB@Inject@ManagedProperty

您想要的是在调用操作阶段调用的方法。您需要或者TestSessionBB 的ajax 侦听器方法中完成这项工作:

@Inject
private TestRequestBB testRequestBB;

public void next() { // No, no need to return String.
    // ...

    testRequestBB.initialize();
}

&lt;f:event type="preRenderView"&gt; 添加到视图中。这允许您在调用操作阶段的最后,就在呈现响应阶段之前执行 bean 方法。把它放在你的视野中:

<f:event type="preRenderView" listener="#{testRequestBB.initialize}" />

不要忘记从方法中删除 @PostConstruct 注释。

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

<h:dataTable value="#{testRequestBB.testList}" ...>

如果你真的,真的,需要在@PostConstruct 中获取请求参数,那么你可以通过ExternalContext#getRequestParameterMap() 手动获取它们:

@PostConstruct
public void initialize() {
    String foo = (String) FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("foo");
    // ...
}

如果您使用 JSF2 @ManagedBean 注释而不是 CDI @Named 注释,您也可以使用 @ManagedProperty 来代替:

@ManagedProperty("#{param.foo}")
private String foo;

此值将在@PostConstruct 期间可用。 CDI中没有这样的注解,但是如果有需要你可以自己homegrow一个。

另见:

【讨论】:

  • 昨天晚上我在最初的研究中遇到了 preRenderView-listener。直接在 jsf 阶段工作的方法起初似乎有点“hacky”;)我预计肯定还有别的东西。无论如何...再次感谢您的详细帮助。
  • @lostiniceland:您最初的业务工作只需要在 ajax 侦听器方法中完成。你想调用一个业务操作,action(listener) 方法就是为此而生的。然而,更好的是使用单个视图范围的 bean,这样您就不需要将请求和会话范围的 bean 混合在一起。在会话范围的 bean 中存储特定于视图的状态是一个糟糕的设计。如果您使用的是 JSF2 注释,则可以为此使用 @ViewScoped。在 CDI 中,您可以使用 @ConversationScoped 进行模拟。
  • 如果每个@Named 产生一个完全独立的实例,如何确保视图使用正确的@Named?
  • @thufir:只需指定一个并指定适当的范围。
猜你喜欢
  • 2012-02-04
  • 2019-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-02
  • 1970-01-01
  • 2012-03-03
相关资源
最近更新 更多