Seam 2.3 + JSF + AJAX。当重新渲染一个面板时,其他面板中的方法也会被调用

Seam 2.3 + JSF + AJAX. When re-rendering one panel methods in other panel get called too

我的项目是使用 JBoss Seam 2.3、JSF 2.1 和 richfaces 构建的。在使用 richfaces aj4 调用重新渲染组件时,有些事情我不完全理解。

当使用 a4j 执行某些操作并且渲染属性的值指向某些 h:panelGroup id 时,也会调用其他面板中的方法。

我准备了一些代码来演示这一点。

BeanA.java。模拟数据访问。

package test;

import java.util.Arrays;
import java.util.List;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.log.Log;

@Scope(ScopeType.CONVERSATION)
@Name("beanA")
public class BeanA {

    @Logger
    private Log log;

    public List<Integer> list() {
        log.info("beanA.list()");
        // DB Query simulation
        Integer[] result = {1,2,3};
        return Arrays.asList(result);
    }

}

BeanB.java。简单的点击计数器。

package test;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.log.Log;

@Scope(ScopeType.CONVERSATION)
@Name("beanB")
public class BeanB {

    @Logger
    private Log log;

    private int counter = 0;

    public void dumbAction() {
        // do nothing
        log.info("beanB.dumbAction()");
        counter++;
    }

    public int getCounter() {
        return counter;
    }

    public void setCounter(int counter) {
        this.counter = counter;
    }

}

test.xhtml

<f:view xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:a="http://richfaces.org/a4j"
    xmlns:richext="http://java.sun.com/jsf/composite/richext"
    xmlns:s="http://jboss.org/schema/seam/taglib" contentType="text/html"
        xmlns:c="http://java.sun.com/jsp/jstl/core">
    <h:html>
    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>TEST</title>
    </h:head>
    <h:body>

        <h:panelGroup id="listPanel">
            <ul>
                <li>
                    <h:outputText value="a:repeat" />
                </li>
                <a:repeat value="#{beanA.list()}" var="i">
                    <li>
                        <h:outputText value="#{i}" />
                    </li>
                </a:repeat>
            </ul>
<!--            <ul> -->
<!--                <li> -->
<!--                    <h:outputText value="ui:repeat" /> -->
<!--                </li> -->
<!--                <ui:repeat value="#{beanA.list()}" var="i"> -->
<!--                    <li> -->
<!--                        <h:outputText value="#{i}" /> -->
<!--                    </li> -->
<!--                </ui:repeat> -->
<!--            </ul> -->
<!--            <ul> -->
<!--                <li> -->
<!--                    <h:outputText value="c:foreach" /> -->
<!--                </li> -->
<!--                <c:forEach items="#{beanA.list()}" var="i"> -->
<!--                    <li> -->
<!--                        <h:outputText value="#{i}" /> -->
<!--                    </li> -->
<!--                </c:forEach> -->
<!--            </ul> -->
        </h:panelGroup>

        <h:form>
            <a:commandLink value="Dumb action" action="#{beanB.dumbAction()}"
                execute="@this" render="timesDumbActionPanel" />
        </h:form>

        <h:panelGroup id="timesDumbActionPanel">
            <h:outputText value="#{beanB.counter}" />
        </h:panelGroup>
    </h:body>
    </h:html>
</f:view>

当我加载页面时 /test.seam beanA.list() 被调用一次。当我单击 "Dumb action" link 时,会记录以下内容:

19:28:21,461 INFO  [test.BeanA] (http--0.0.0.0-80-1) beanA.list()
19:28:21,461 INFO  [test.BeanB] (http--0.0.0.0-80-1) beanB.dumbAction()
19:28:21,461 INFO  [test.BeanA] (http--0.0.0.0-80-1) beanA.list()

beanA.list() 被调用了两次,我不明白为什么。当使用 ui:repeat 而不是 a:repeat 时,beanA.list() 被调用了 11 次。我也尝试过更改范围,但结果是一样的。

我期望的行为只是调用 beanB.dumbAction() 并重新渲染面板以刷新计数器。

我知道 getter 被多次调用,但这是否也适用于所有方法?

我为避免这种情况所做的只是为列表创建一个字段并修改 getter 以初始化它(如果不是)。像这样:

@Name("beanA")
public class BeanA {

    @Logger
    private Log log;

    private List<Integer> list;

    public void initList() {
        log.info("beanA.list()");
        // DB Query simulation
        Integer[] result = { 1, 2, 3 };
        setList(Arrays.asList(result));
    }

    public List<Integer> getList() {
        if (list == null)
            initList();
        return list;
    }

    public void setList(List<Integer> list) {
        this.list = list;
    }

}

getter 仍会被调用,但不会调用 initList(),这很好。这样我就避免了执行不必要的查询。唯一的问题是数据更改时。我需要再次手动调用 initList() 并且仅仅重新渲染它的面板是不够的。

也许我遗漏了 JSF 生命周期之类的东西,你能给我一些提示吗?谢谢!!

I do know getters are called multiple times, but does this apply to all methods too?

它不像 JSF 知道特定方法是 getter。如果您在需要数据的地方使用,例如@value 那么它是一个 getter.

您看到多次调用 getters 的原因是当 JSF/RichFaces 正在寻找某个元素时,它必须遍历它之前的所有元素。在你的情况下,为了找到 timesDumbActionPanel 它必须经过 listPanel 并检查 a4j:repeat.

The only problem is when data changes. I need to manually call initList() again and it's not enough with just re-rendering it's panel.

您可以从任何正在更改数据的方法中调用 initList()