O JSF (Java Server Faces) é um poderoso framework para desenvolvimento de aplicações Web em Java, veio com o intuito de substituir a “velha” interface em JSP por uma mais moderna e de fácil utilização, assim o desenvolvedor não precisa se preocupar com códigos HTML, layout, entre outros aspectos que fogem do real objetivo da aplicação.

Para se utilizar o JSF é necessário o entendimento do seu ciclo de vida, assim o desenvolvedor poderá ter noção do que acontece de forma transparente porém muito importante.

O JSF possui 6 ciclos, são eles:

  • Restore View
  • Apply Request View
  • Process Validations
  • Update Model Values
  • Invoke Application
  • Render Response

Explicaremos cada fase através de exemplos práticos do JSF, mas antes precisaremos configurar o Listener que será responsável por monitorar essa mudança de estados do JSF, para isso implementaremos uma interface chamada PhaseListener. Veja na listagem 1 como ficará nossa interface.


package br.com.debugjsf.listener;

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

public class LifeCycleListener implements PhaseListener {

    public PhaseId getPhaseId() {
        return PhaseId.ANY_PHASE;
    }

    public void beforePhase(PhaseEvent event) {
        System.out.println("INICIANDO FASE: " + event.getPhaseId());
    }

    public void afterPhase(PhaseEvent event) {
        System.out.println("FINALIZANDO FASE: " + event.getPhaseId());
    }

}
Listagem 1. Implementando a Interface PhaseListener

Vamos a explicação do código acima: Criamos uma classe que implementa o PhaseListener, nesta temos os seguintes métodos:

  • getPhaseId(): Apenas retorna um PhaseId que pode ser um daqueles 6 que citamos logo no inicio (1 - Restore View, 2 - Apply Request View, 3 - Process Validations, 4 - Update Model Values, 5 - Invoke Application e 6 - Render Response).
  • beforePhase(): Antes de uma determinada fase iniciar este método é chamado.
  • afterPhase(): Depois de uma determinada fase terminar este método é chamado.

Após termos nosso PhaseListener implementado, vamos configurar o nosso arquivo faces-config.xml para dizer a nossa aplicação quem será o nosso Listener, ou seja, apontar qual será a classe responsável por escutar as mudanças de fase do JSF. Veja na listagem 2 como ficou a configuração do nosso faces-config.xml.


<lifecycle>
    <phase-listener>br.com.debugjsf.listener.LifeCycleListener</phase-listener>
</lifecycle>
Listagem 2. Configurando faces-config.xml

Temos enfim nossa aplicação configurada para escutar todas as fases do JSF, partiremos agora para os exemplos em cada fase. A saída de ciclo completo do JSF no console deve ser algo parecido com a listagem 3.


INICIANDO FASE: RESTORE_VIEW 1
	FINALIZANDO FASE: RESTORE_VIEW 1
	INICIANDO FASE: APPLY_REQUEST_VALUES 2
	FINALIZANDO FASE: APPLY_REQUEST_VALUES 2
	INICIANDO FASE: PROCESS_VALIDATIONS 3
	FINALIZANDO FASE: PROCESS_VALIDATIONS 3
	INICIANDO FASE: UPDATE_MODEL_VALUES 4
	FINALIZANDO FASE: UPDATE_MODEL_VALUES 4
	INICIANDO FASE: INVOKE_APPLICATION 5
	FINALIZANDO FASE: INVOKE_APPLICATION 5
	INICIANDO FASE: RENDER_RESPONSE 6
	FINALIZANDO FASE: RENDER_RESPONSE 6
Listagem 3. Saída no console de um ciclo completo JSF

Configurando a aplicação de exemplo no JSF

Vamos primeiramente configurar nossa página XHTML, que usaremos para realizar a depuração.


<h:form>
<h:inputText
    binding="#{myBean.inputComponent}"
    value="#{myBean.inputValue}"
    valueChangeListener="#{myBean.inputChanged}">
    <f:converter converterId="myConverter" />
    <f:validator validatorId="myValidator" />
</h:inputText>
<h:commandButton
    value="submit"
    action="#{myBean.action}" />
<h:outputText
    binding="#{myBean.outputComponent}"
    value="#{myBean.outputValue}" />
<h:messages />
</h:form>
Listagem 4. teste.xhtml

Vamos as explicações:

  • input text: Este componente está realizando um binding com a propriedade inputComponent do nosso ManagedBean, seu valor é armazenado na propriedade inputValue do ManagedBean, temos ainda um listener associado a ele (inputChanged) que escuta toda vez que alguma alteração é realizada no valor deste campo. Para finalizar este componente, temos ainda um conversor e um validador associados ao mesmo.
  • commandButton: O nosso commandButton dispensa explicações, ele apenas aciona um action em nosso ManagedBean.
  • outputText: Por fim, este componente que é apenas um “label” possui um binding também em nosso ManagedBean e um atributo associado ao seu valor.

Vamos agora ver como será nosso ManagedBean.


package br.com.debugjsf.mb;

import javax.faces.component.html.HtmlInputText;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.event.ValueChangeEvent;

public class MyBean {

    //Componentes para Binding
    private HtmlInputText inputComponent;
    private HtmlOutputText outputComponent;

    //Armazena valores dos componentes
    private String inputValue;
    private String outputValue;

    public MyBean() {
        log("constructed");
    }

    public void action() {
        outputValue = inputValue;
        log("succes");
    }

    public HtmlInputText getInputComponent() {
        log(inputComponent);
        return inputComponent;
    }

    public String getInputValue() {
        log(inputValue);
        return inputValue;
    }

    public HtmlOutputText getOutputComponent() {
        log(outputComponent);
        return outputComponent;
    }

    public String getOutputValue() {
        log(outputValue);
        return outputValue;
    }

    public void setInputComponent(HtmlInputText inputComponent) {
        log(inputComponent);
        this.inputComponent = inputComponent;
    }

    public void setInputValue(String inputValue) {
        log(inputValue);
        this.inputValue = inputValue;
    }

    public void setOutputComponent(HtmlOutputText outputComponent) {
        log(outputComponent);
        this.outputComponent = outputComponent;
    }
      
      //Escuta por alterações do inputText e escreve no console quando houver
    public void inputChanged(ValueChangeEvent event) {
        log(event.getOldValue() + " to " + event.getNewValue());
    }

    //Escreve um LOG no console, para acompanharmos o ciclo de vida    
    private void log(Object object) {
        String methodName = Thread.currentThread().getStackTrace()[2].getMethodName();
        System.out.println("MyBean " + methodName + ": " + object);
    }
}
Listagem 5. MyBean.java

E no nosso faces-config.xml configuraremos o ManagedBean assim como na listagem abaixo.


<converter>
    <converter-id>myConverter</converter-id>
    <converter-class>br.com.debugjsf.util.MyConverter</converter-class>
</converter>
<validator>
    <validator-id>myValidator</validator-id>
    <validator-class>br.com.debugjsf.util.MyValidator</validator-class>
</validator>
<managed-bean>
    <managed-bean-name>myBean</managed-bean-name>
    <managed-bean-class>br.com.debugjsf.mb.MyBean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
</managed-bean>
Listagem 6. Configurando faces-config.xml para o ManagedBean

Vamos recapitular o que temos até agora:

  • Configuramos o nosso PhaseListener para escutar as alterações de fase do JSF
  • Criamos uma aplicação exemplo para testar nosso PhaseListener, onde essa aplicação é composta por: 1 view (teste.xhtml), 1 ManagedBean (MyBean.java), 1 Converter e 1 Validator. Então vamos agorar criar nosso Converter e nosso Validator.

package br.com.debugjsf.util;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;

public class MyConverter implements Converter {

    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        System.out.println("MyConverter getAsObject: " + value);
        return value;
    }

    public String getAsString(FacesContext context, UIComponent component, Object value) {
        System.out.println("MyConverter getAsString: " + value);
        return (String) value;
    }

}
Listagem 7. Criando o Converter da nossa aplicação

package br.com.debugjsf.util;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

public class MyValidator implements Validator {

    public void validate(FacesContext context, UIComponent component, Object value)
        throws ValidatorException
    {
        System.out.println("MyValidator validate: " + value);
    }

}
Listagem 8. Criando o Validator da nossa aplicação

Enfim temos nossa aplicação totalmente criada e configurada para escutar as fases do JSF, é óbvio que você pode adaptar o PhaseListener para sua aplicação, colocamos esta apenas para exemplificar o uso deste.

Conclusão

Enfim, o objetivo deste artigo não é explicar com detalhes o ciclo de vida do JSF, e sim configurar um depurador para acompanhar na prática o ciclo de vida deste framework, pois melhor que a teoria, é a prática. Tendo o conhecimento básico de cada ciclo de vida e acompanhando este depurador, você conseguirá ter uma visão melhor de como funciona tal mecanismo e verá a teoria se aplicando na prática.

Isso porque muitos desenvolvedores sentem dificuldade de “ver” como funciona o ciclo de vida do JSF, pois este é totalmente transparente ao desenvolvedor.