¿Cómo funciona el atributo 'enlace' en JSF? ¿Cuándo y cómo se debe utilizar?

78

Hay muchos materiales que diferencian valueatributos y bindingatributos en JSF.

Me interesa saber en qué se diferencian ambos enfoques. Dado:

public class User {
    private String name;
    private UICommand link;

    // Getters and setters omitted.
}
<h:form>
    <h:commandLink binding="#{user.link}" value="#{user.name}" />
</h:form>

Es bastante sencillo lo que sucede cuando valuese especifica un atributo. El captador se ejecuta para devolver el namevalor de propiedad del Userbean. El valor se imprime en la salida HTML.

Pero no pude entender cómo bindingfunciona. ¿Cómo mantiene el HTML generado un enlace con la linkpropiedad del Userbean?

A continuación se muestra la parte relevante de la salida generada después del embellecimiento manual y los comentarios (tenga en cuenta que la identificación j_id_jsp_1847466274_1se generó automáticamente y que hay dos widgets de entrada ocultos). Estoy usando JSF RI de Sun, versión 1.2.

<form action="/TestJSF/main.jsf" enctype="application/x-www-form-urlencoded"
    id="j_id_jsp_1847466274_1" method="post"  name="j_id_jsp_1847466274_1">
    <input name="j_id_jsp_1847466274_1" type="hidden" value="j_id_jsp_1847466274_1">
    <a href="#" onclick="...">Name</a>
    <input autocomplete="off" id="javax.faces.ViewState" name="javax.faces.ViewState"
        type="hidden" value="-908991273579182886:-7278326187282654551">
</form>

¿Dónde está bindingalmacenado aquí?

John
fuente

Respuestas:

132

¿Como funciona?

Cuando se crea / restaura una vista JSF (archivo Facelets / JSP), se generará un árbol de componentes JSF. En ese momento, el tiempo de construcción de la vista , bindingse evalúan todos los atributos ( junto con los idatributos y los controladores de etiquetas como JSTL ). Cuando es necesario crear el componente JSF antes de agregarlo al árbol de componentes, JSF verificará si el bindingatributo devuelve un componente creado previamente (es decir, no null) y, si es así, lo utilizará. Si no está creado previamente, JSF creará automáticamente el componente "de la forma habitual" e invocará el bindingatributo de establecimiento detrás con la instancia del componente creado automáticamente como argumento.

En efecto, vincula una referencia de la instancia del componente en el árbol de componentes a una variable de ámbito. Esta información no es visible de ninguna manera en la representación HTML generada del componente en sí. De todos modos, esta información no es relevante para la salida HTML generada. Cuando se envía el formulario y se restaura la vista, el árbol de componentes JSF simplemente se reconstruye desde cero y todos los bindingatributos se volverán a evaluar como se describe en el párrafo anterior. Después de que se vuelva a crear el árbol de componentes, JSF restaurará el estado de la vista JSF en el árbol de componentes.

¡Las instancias de componentes tienen un alcance de solicitud!

Es importante saber y comprender que las instancias de componentes concretos tienen un alcance de solicitud efectivo. Se crean nuevamente en cada solicitud y sus propiedades se llenan con valores del estado de vista JSF durante la fase de restauración de la vista. Por lo tanto, si vincula el componente a una propiedad de un bean de respaldo, entonces el bean de respaldo no debe estar en un alcance más amplio que el alcance de la solicitud. Consulte también el capítulo 3.1.5 de la especificación JSF 2.0 :

3.1.5 Enlaces de componentes

...

Los enlaces de componentes se utilizan a menudo junto con JavaBeans que se instancian dinámicamente a través de la función de creación de Managed Bean (consulte la Sección 5.8.1 “VariableResolver y Default VariableResolver”). Se recomienda encarecidamente que los desarrolladores de aplicaciones coloquen beans gestionados a los que apuntan las expresiones de enlace de componentes en el ámbito de "solicitud". Esto se debe a que colocarlo en el ámbito de la sesión o la aplicación requeriría seguridad de subprocesos, ya que las instancias de UIComponent dependen de que se ejecuten dentro de un solo subproceso. También hay impactos potencialmente negativos en la gestión de la memoria cuando se coloca un enlace de componente en el ámbito de la "sesión".

De lo contrario, las instancias de componentes se comparten entre varias solicitudes, lo que posiblemente da como resultado errores de " ID de componente duplicado " y comportamientos "extraños" porque los validadores, convertidores y oyentes declarados en la vista se vuelven a adjuntar a la instancia de componente existente de solicitudes anteriores. Los síntomas son claros: se ejecutan varias veces, una vez más con cada solicitud dentro del mismo alcance al que está vinculado el componente.

Y, bajo una carga pesada (es decir, cuando varias solicitudes HTTP (subprocesos) diferentes acceden y manipulan la misma instancia de componente al mismo tiempo), tarde o temprano puede enfrentar un bloqueo de la aplicación con, por ejemplo, un subproceso atascado en UIComponent.popComponentFromEL o Java Threads al 100% de utilización de la CPU utilizando richfaces UIDataAdaptorBase y su HashMap interno , o incluso algunos "extraños" IndexOutOfBoundsExceptiono que ConcurrentModificationExceptionvienen directamente del código fuente de implementación JSF mientras JSF está ocupado guardando o restaurando el estado de la vista (es decir, el seguimiento de la pila indica saveState()o restoreState()métodos y similares).

Usar bindinguna propiedad de frijol es una mala práctica

Independientemente, de bindingesta manera, vincular una instancia de componente completa a una propiedad de bean, incluso en un bean con alcance de solicitud, es en JSF 2. un caso de uso bastante raro y generalmente no es la mejor práctica. Indica un olor a diseño. Normalmente se declara componentes en la vista lateral y se unen a sus atributos como el tiempo de ejecución value, y tal vez otros como styleClass, disabled, rendered, etc, a las propiedades normales de frijol. Luego, simplemente manipule exactamente la propiedad del bean que desea en lugar de tomar todo el componente y llamar al método setter asociado con el atributo.

En los casos en que necesita un componente que se "construyen dinámicamente" basado en un modelo estático, mejor es utilizar vista etiquetas de tiempo de compilación como JSTL , si es necesario en un archivo de etiquetas , en lugar de createComponent(), new SomeComponent(), getChildren().add()y lo que no. Consulte también ¿Cómo refactorizar un fragmento de JSP antiguo a algún equivalente de JSF?

O bien, si un componente necesita ser "prestados de forma dinámica", basada en un modelo dinámico, a continuación, sólo tiene que utilizar un componente iterador ( <ui:repeat>, <h:dataTable>, etc.). Consulte también Cómo agregar componentes JSF de forma dinámica .

Los componentes compuestos son una historia completamente diferente. Es completamente legítimo vincular componentes dentro de un <cc:implementation>al componente de respaldo (es decir, el componente identificado por <cc:interface componentType>. Vea también ao Dividir java.util.Date en dos campos h: inputText que representan hora y minuto con f: convertDateTime y Cómo implementar una lista dinámica con ¿Un componente compuesto JSF 2.0?

Usar solo bindingen ámbito local

Sin embargo, a veces le gustaría saber sobre el estado de un componente diferente desde dentro de un componente en particular, más que a menudo en casos de uso relacionados con la validación dependiente de acción / valor. Para eso, bindingse puede utilizar el atributo, pero no en combinación con una propiedad de bean. Puede especificar un nombre de variable único en el ámbito de EL local en el bindingatributo así binding="#{foo}"y el componente está durante la respuesta de renderizado en otro lugar en la misma vista directamente como UIComponentreferencia disponible por #{foo}. Aquí hay varias preguntas relacionadas en las que se ha utilizado dicha solución en la respuesta:

Ver también:

BalusC
fuente
Establecí al usuario como un bean con alcance de solicitud. Y probé sysout en los métodos getlink y setlink. Al aterrizar en la página = User.User(), User.getLink(), User.setLink(), User.getValue()Cuando hago clic en el enlace =User.User(), User.setLink()...
John
¿Por qué se llama de nuevo a setLink () y se crea otro objeto UICommand cuando ya tiene uno?
Juan
"si enlaza [...], entonces el bean de respaldo no debe estar en un alcance más amplio que el alcance de la solicitud". ¿Esto sigue siendo cierto en JSF 2.2? ¿Cuál es la mejor opción si tengo que incluir componentes programáticamente en la página usando getChildren (). AddAll (...)?
RinaldoPJr
@Rinaldo: use JSTL para crear la vista mediante programación en solo sintaxis XML.
BalusC
1
@Shirgill: De hecho. UIViewRoot(todo UIComponentbásicamente) no se puede serializar de todos modos.
BalusC
1

cada componente JSF se representa a sí mismo en HTML y tiene un control completo sobre el HTML que produce. Hay muchos trucos que JSF puede usar, y exactamente cuál de esos trucos se usará depende de la implementación de JSF que esté utilizando.

  • Asegúrese de que cada entrada de from tenga un nombre totalmente único, de modo que cuando el formulario se envíe al árbol de componentes que lo generó, sea fácil saber dónde cada componente puede leer su formulario de valor.
  • El componente JSF puede generar javascript que se envía al seridor, el javascript generado también sabe dónde está vinculado cada componente, porque fue generado por el componente.
  • Para cosas como hlink, puede incluir información vinculante en la URL como parámetros de consulta o como parte de la propia URL o como parámetros matrx. por ejemplo.

    http:..../somelink?componentId=123permitiría a jsf mirar en el árbol de componentes para ver que se hizo clic en el enlace 123. o podría ehtp:..../jsf;LinkId=123

La forma más sencilla de responder a esta pregunta es crear una página JSF con un solo enlace y luego examinar la salida html que produce. De esa manera, sabrá exactamente cómo sucede esto usando la versión de JSF que está usando.

ams
fuente
Diría que solo he usado el enlace de componentes al generar el componente dinámicamente en el lado del servidor, estableciendo todos los atributos como actiony value, y luego dejé que el marco JSF haga su trabajo.
Luiggi Mendoza
Lo almacené usercomo bean administrado con ámbito de aplicación y cuando hago clic en el enlace cada vez, solo el segundo número en los value="-908991273579182886:-7278326187282654551"cambios y todo lo demás es igual. Me pregunto qué magia hacen estos.
John