Como Depurar o Ciclo de Vida do JSF (Java Server Faces)
Veja neste artigo como depurar as 6 fases do ciclo de vida do JSF (Java Server Faces) para entender como o JSF se comporta por trás dos panos.
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());
}
}
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>
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
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>
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);
}
}
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>
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;
}
}
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);
}
}
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.
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Vídeo