【问题标题】:How to create a variable in build time?如何在构建时创建变量?
【发布时间】:2015-09-17 20:07:18
【问题描述】:

我有这个页面:

<c:forEach begin="0" end="#{tareaController.tipoFuncion.filas}" step="1" var="x">
    <p:row>
        <c:forEach begin="0" end="#{tareaController.tipoFuncion.columnas}" step="1" var="y">
            <c:choose>
                <c:when test="#{tareaController.configuracionEnPosicion(x,y) ne null}">
                    <p:column style="width: 80px" 
                              colspan="#{tareaController.configuracionEnPosicion(x,y).colSpan}"
                              rowspan="#{tareaController.configuracionEnPosicion(x,y).rowSpan}">
                        <p:outputLabel value="#{tareaController.atributoEnPosicion(x,y).labelAtributo}:" 
                                       rendered="#{!tareaController.validar.isBoton(tareaController.atributoEnPosicion(x, y).tipoAtributosOpcionales)}">
                            <c:if test="#{tareaController.configuracionEnPosicion(x,y).esObligatorio}">
                                *
                            </c:if>
                        </p:outputLabel>
                    </p:column>
                    <p:column colspan="#{tareaController.configuracionEnPosicion(x,y).colSpan}"
                              rowspan="#{tareaController.configuracionEnPosicion(x,y).rowSpan}"> 
                        <c:choose>
                            <c:when test="#{tareaController.validar.isSeparador(tareaController.atributoEnPosicion(x, y).tipoAtributosOpcionales)}">
                                <p:separator/>
                            </c:when>
                            <c:when test="#{tareaController.validar.isNumerico(tareaController.atributoEnPosicion(x,y).tipoAtributosOpcionales)}">
                                <pe:inputNumber decimalSeparator="#{tareaController.configuracionEnPosicion(x,y).separadorDecimal}" 
                                                thousandSeparator="#{tareaController.configuracionEnPosicion(x,y).separadorMiles}" 
                                                decimalPlaces="#{tareaController.configuracionEnPosicion(x,y).cantidadDecimales}" 
                                                disabled="#{mFuncion.soloLectura || !tareaController.tareaAceptada || tareaController.tomadaPorOtroUsuario || tareaController.configuracionEnPosicion(x,y).soloLectura}"
                                                maxValue="#{tareaController.configuracionEnPosicion(x,y).valorMaximo}"
                                                rendered="#{!tareaController.configuracionEnPosicion(x,y).ocultarAtributo}"
                                                value="#{tareaController.atributoEnPosicion(x,y).valorAtributoNumerico}" 
                                                style="width: #{tareaController.configuracionEnPosicion(x,y).ancho}px; height: #{tareaController.configuracionEnPosicion(x,y).alto}px;">
                                    <p:ajax global="false" update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:nombreRepetido, :f_tarea:b_completarT" listener="#{tareaController.validarTarea()}"/>
                                </pe:inputNumber>
                            </c:when>
                            <c:when test="#{tareaController.validar.isBoton(tareaController.atributoEnPosicion(x, y).tipoAtributosOpcionales)}">
                                <p:commandButton value="#{tareaController.atributoEnPosicion(x,y).labelAtributo}" 
                                                 actionListener="#{tareaController.ejecutarWs(tareaController.configuracionEnPosicion(x,y))}"
                                                 update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:pg_funcion_#{tareaController.indiceTramite}"/>
                            </c:when>
                            <c:when test="#{tareaController.validar.isFecha(tareaController.atributoEnPosicion(x,y).tipoAtributosOpcionales)}">
                                <p:calendar value="#{tareaController.atributoEnPosicion(x,y).valorAtributoFecha}" 
                                            maxdate="#{tareaController.configuracionEnPosicion(x,y).fechaMaxima}" 
                                            mindate="#{tareaController.configuracionEnPosicion(x,y).fechaMinima}"
                                            rendered="#{!tareaController.configuracionEnPosicion(x,y).ocultarAtributo}"
                                            disabled="#{mFuncion.soloLectura || !tareaController.tareaAceptada || tareaController.tomadaPorOtroUsuario || tareaController.configuracionEnPosicion(x,y).soloLectura || tareaController.atributoEnPosicion(x,y).bloquear}"
                                            size="20"
                                            pattern="#{tareaController.configuracionEnPosicion(x,y).formatoFecha}">
                                    <p:ajax global="false" update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:nombreRepetido, :f_tarea:b_completarT" listener="#{tareaController.validarTarea()}"/>
                                    <p:ajax event="dateSelect" global="false" update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:nombreRepetido, :f_tarea:b_completarT" listener="#{tareaController.validarTarea()}"/>
                                </p:calendar>
                            </c:when>
                            <c:otherwise test="#{tareaController.validar.isAlfanumerico(tareaController.atributoEnPosicion(x,y).tipoAtributosOpcionales)}">
                                <p:inputTextarea value="#{tareaController.atributoEnPosicion(x,y).valorAtributoAlfanumerico}" 
                                                 disabled="#{mFuncion.soloLectura || !tareaController.tareaAceptada || tareaController.tomadaPorOtroUsuario || tareaController.configuracionEnPosicion(x,y).soloLectura}"
                                                 rendered="#{!tareaController.configuracionEnPosicion(x,y).ocultarAtributo}"
                                                 style="width: #{tareaController.configuracionEnPosicion(x,y).ancho}px; 
                                                 height:  #{tareaController.configuracionEnPosicion(x,y).alto}px; 
                                                 word-break: normal;">
                                    <p:ajax global="false" update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:nombreRepetido, :f_tarea:b_completarT" listener="#{tareaController.validarTarea()}"/>
                                    <p:ajax event="select" global="false" update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:nombreRepetido, :f_tarea:b_completarT" listener="#{tareaController.validarTarea()}"/>
                                </p:inputTextarea>
                            </c:otherwise>
                        </c:choose>
                    </p:column>
                </c:when>
                <c:otherwise>
                    <p:column>
                        <h:panelGroup/>
                    </p:column>
                    <p:column>
                        <h:panelGroup/>
                    </p:column>
                </c:otherwise>
            </c:choose>
        </c:forEach>

我的想法是我可以配置该页面并将组件放置在我想要的位置,并使用许多不同的配置。它工作得很好,人们对此很满意,但我不满意。如果您在页面上查看,我会在循环中的每次运行中调用 configuracionEnPosicion(x,y) 和 atributoEnPosicion(x,y) 方法 300 次。这些方法像其他 3 种方法一样调用并执行 foreach 以在 x,y 中查找实际配置。令人惊讶的是,它的运行速度并不慢,但我觉得这是我可以避免的很多开销。

在我尝试将 configuracionEnPosicion(x,y) 和 atributoEnPosicion(x,y) 的结果与它们各自的 getter 和 setter 一起保存在客户端的变量中之前,但是由于 JSTL Core 标记在构建时运行,因此在渲染时间该变量未更新,导致该变量保持其最后一个值并给出不希望的行为。我的“解决方案”是实际的,但我的最后一个问题是:有没有办法存储该变量的值,所以我只能在构建时保持它的生命或类似的东西?我想在每个 x 和 y 位置只调用一次这些方法,但我对 JSF 和 Primefaces 的了解不足以实现这一点。

附加信息:

  • Apache Tomcat 7.0.41。
  • JSF 2.2
  • Primefaces 4.0
  • Mojarra 2.7.2
  • jstl-1.2

【问题讨论】:

  • configuracionEnPosicion 的返回类型是什么?
  • 抱歉,之前没有读过这个:一个 JPA 实体。所以,一个带有数据的 POJO。

标签: jsf jsf-2 primefaces


【解决方案1】:

看起来像是JSR 107: JCACHE - Java Temporary Caching API 的任务。但是您应该使用 CDI bean 而不是 JSF 托管 bean。这允许您编写如下代码:

 @CacheResult(cacheName = "configuracionEnPosicion")
 public String configuracionEnPosicion(int x, int y){
    .....
 }

还需要创建 META-INF/beans.xml:

 <beans xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
    <interceptors>
         <class>org.jsr107.ri.annotations.cdi.CacheResultInterceptor</class>
         <class>org.jsr107.ri.annotations.cdi.CachePutInterceptor</class>
         <class>org.jsr107.ri.annotations.cdi.CacheRemoveEntryInterceptor</class>
         <class>org.jsr107.ri.annotations.cdi.CacheRemoveAllInterceptor</class>
    </interceptors>
</beans>

还需要在某处创建缓存:

@PostConstruct
public void init() {
    try {
        // Retrieve the system wide cache manager
        CacheManager cacheManager = Caching.getCachingProvider().getCacheManager();
        // Define a named cache with default JCache configuration
        MutableConfiguration<String, String> configuration = new MutableConfiguration<>();
        //specify expiry policy
        configuration.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ONE_MINUTE));
        cache = cacheManager.createCache("configuracionEnPosicion", configuration);
    } catch (Exception e) {
        L.error("Can't init cache!", e);
    }
}

当然,您应该选择 jcache 提供程序(ecache、infinispan、...)并向项目添加必要的依赖项。

【讨论】:

    【解决方案2】:

    我在@BalusC cmets 之后编辑了我的答案

    您可以预先计算支持 bean PostConstruct 方法中的所有值。

    • 在支持 bean 级别声明一个二维数组:

      private ReturnedObjectClass[][] precalculatedArray;
      
    • 添加getter方法。

    • 在 PostConstruct 方法中初始化它:

      precalculatedArray = new ReturnedObjectClass[x][y];
      
    • 用你的函数在 PostConstruct 方法中返回的对象填充它:

      precalculatedArray[i][j] = myBigFunction(i,j);
      

    然后您可以使用以下方法从您的页面访问计算值:

        value="#{tareaController.precalculatedArray[x][y].propertyOfReturnedObjectClass}"
    

    【讨论】:

    • 显式放入请求范围很笨拙。只要确保 bean 在正确的范围内。
    • @BalusC,Flash 范围可以成为更好的选择吗?
    • 为什么?只需将 bean 放在正确的范围内,只需使用 #{tareaController.precalculatedArray}
    • 是的,你是对的。我试图在内存中尽可能少地维护计算出的数组,但正确的方法是赋予它与支持 bean 相同的生命周期。我将编辑我的答案。谢谢。
    【解决方案3】:

    你可以使用c:set

    <c:set var="salary" scope="request" value="#{calculatePosition(x,y)}"/>
    

    或者也许您可以使用varstatus,具体取决于您的 fct 中发生的情况。 (这是 jsf 标签而不是 jstl 标签的文档,但它是同一件事)。

    【讨论】:

    • The &lt;c:set&gt; without a scope is just an alias。 OP 希望避免每次都为相同的参数重新执行 calculatePosition() 方法中相同的 - 显然很昂贵 - 任务。
    • 那么,如果我们使用scope="request",它会在不重新评估的情况下工作吗?
    • 不。仅仅是因为 x 和 y 在每次迭代期间都会发生变化,而不是在每次请求期间。
    • @BalusC 好吧,所以范围在这里无关紧要,无论如何它都行不通。
    • 好的,但您不需要评估所有这些时间。在 c:forEach 循环内,xy 不会改变,因此我们可以通过在循环内使用 var 来受益。是否有任何范围会强制每个 c:forEach 重新评估一次?
    猜你喜欢
    • 1970-01-01
    • 2020-06-30
    • 1970-01-01
    相关资源
    最近更新 更多