Diferenças entre ação e açãoListener

Qual é a diferença entre action e actionListener e quando devo usar action versus actionListener ?

actionListener

Use actionListener se você quiser ter um gancho antes que a ação real do negócio seja executada, por exemplo, para registrá-lo e / ou para definir uma propriedade adicional (por ) e / ou para ter access ao componente que invocou o action (que está disponível no argumento ActionEvent ). Portanto, puramente para fins de preparação antes que a ação real do negócio seja invocada.

O método actionListener possui por padrão a seguinte assinatura:

 import javax.faces.event.ActionEvent; // ... public void actionListener(ActionEvent event) { // ... } 

E deve ser declarado da seguinte maneira, sem nenhum parêntese de método:

  

Note que você não pode passar argumentos adicionais por EL 2.2. No entanto, você pode replace o argumento ActionEvent passando e especificando argumento (s) personalizado (s). Os exemplos a seguir são válidos:

    
 public void methodWithoutArguments() {} public void methodWithOneArgument(Object arg1) {} public void methodWithTwoArguments(Object arg1, Object arg2) {} 

Observe a importância dos parênteses na expressão do método sem argumentos. Se eles estivessem ausentes, o JSF ainda esperaria um método com o argumento ActionEvent .

Se você estiver no EL 2.2+, poderá declarar vários methods de ouvinte de ações por meio da .

     
 public void actionListener1(ActionEvent event) {} public void actionListener2() {} public void actionListener3() {} 

Observe a importância dos parênteses no atributo de binding . Se eles estivessem ausentes, EL confusamente lançaria uma javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean , porque o atributo de binding é, por padrão, interpretado como uma expressão de valor, não como uma expressão de método. Adicionar os parênteses de estilo EL 2.2+ transparentemente transforma uma expressão de valor em uma expressão de método. Veja também ao Por que eu sou capaz de ligar a um método arbitrário se não for suportado pelo JSF?


açao

Use a action se você quiser executar uma ação de negócios e, se necessário, manipular a navegação. O método de action pode (assim, não deve) retornar uma String que será usada como resultado do caso de navegação (a visão de destino). Um valor de retorno de null ou void permitirá que ele retorne à mesma página e mantenha o escopo atual da exibição ativo. Um valor de retorno de uma cadeia vazia ou o mesmo ID da visualização também retornará à mesma página, mas recriará o escopo da visualização e, portanto, destruirá todos os beans com escopo da visualização ativa no momento e, se aplicável, os recriará.

O método de action pode ser qualquer MethodExpression válido, também aqueles que usam argumentos EL 2.2, como abaixo:

  

Com este método:

 public void edit(Item item) { // ... } 

Observe que quando seu método de ação retorna apenas uma string, você também pode especificar exatamente essa string no atributo action . Assim, isso é totalmente desajeitado:

  

Com esse método sem sentido retornando uma string codificada:

 public String goToNextpage() { return "nextpage"; } 

Em vez disso, basta colocar essa string codificada diretamente no atributo:

  

Por favor, note que isto por sua vez indica um mau desenho: navegando pelo POST. Isso não é usuário nem SEO amigável. Isso tudo é explicado em Quando devo usar h: outputLink em vez de h: commandLink? e é suposto ser resolvido como

  

Veja também Como navegar no JSF? Como fazer o URL refletir a página atual (e não a anterior) .


f: ouvinte ajax

Desde o JSF 2.x existe um terceiro caminho, o .

    

O método ajaxListener tem por padrão a seguinte assinatura:

 import javax.faces.event.AjaxBehaviorEvent; // ... public void ajaxListener(AjaxBehaviorEvent event) { // ... } 

Em Mojarra, o argumento AjaxBehaviorEvent é opcional, abaixo funciona bem.

 public void ajaxListener() { // ... } 

Mas no MyFaces, ele lançaria uma MethodNotFoundException . Abaixo, funciona em ambas as implementações do JSF quando você deseja omitir o argumento.

    

Os ouvintes de Ajax não são realmente úteis em componentes de comando. Eles são mais úteis na input e selecionam os componentes / . Nos componentes de comando, basta seguir a action e / ou actionListener para maior clareza e melhor código de autodocumentação. Além disso, como actionListener , o f:ajax listener não suporta retornar um resultado de navegação.

    

Para obter explicações sobre os atributos de execute e render , vá para Understanding PrimeFaces process / update e JSF f: ajax execute / render attributes .


Ordem de invocação

Os actionListener s são sempre invocados antes da action na mesma ordem em que foram declarados na exibição e anexados ao componente. O f:ajax listener é sempre chamado antes de qualquer ouvinte de ação. Então, o seguinte exemplo:

       

Invocará os methods na seguinte ordem:

  1. Bean#ajaxListener()
  2. Bean#actionListener()
  3. ActionListenerType#processAction()
  4. Bean#actionListenerBinding()
  5. Bean#setProperty()
  6. Bean#action()

Manipulação de exceção

O actionListener suporta uma exceção especial: AbortProcessingException . Se essa exceção for lançada a partir de um método actionListener , o JSF ignorará os ouvintes de ação restantes e o método de ação e continuará a renderizar a resposta diretamente. Você não verá uma página de erro / exceção, mas o JSF irá registrá-la. Isso também será implicitamente feito sempre que qualquer outra exceção estiver sendo lançada de um actionListener . Portanto, se você pretende bloquear a página por uma página de erro como resultado de uma exceção de negócios, você definitivamente deve executar a tarefa no método de action .

Se a única razão para usar um actionListener é ter um método void retornando à mesma página, então isso é ruim. Os methods de action também podem perfeitamente retornar void , ao contrário do que alguns IDEs permitem que você acredite através da validação EL. Observe que os exemplos de demonstração do PrimeFaces estão repletos desse tipo de actionListener em todo lugar. Isso é de fato errado. Não use isso como uma desculpa para fazer isso sozinho.

Em pedidos de ajax, no entanto, é necessário um manipulador de exceção especial. Isso é independente de você usar o atributo listener de ou não. Para obter explicações e um exemplo, vá para Manipulação de Exceção em Pedidos do JSF ajax .

Como o BalusC indicou, o actionListener por padrão engloba exceções, mas no JSF 2.0 há um pouco mais disso. Ou seja, não apenas engole e registra, mas publica a exceção.

Isso acontece por meio de uma chamada assim:

 context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, new ExceptionQueuedEventContext(context, exception, source, phaseId) ); 

O ouvinte padrão desse evento é o ExceptionHandler que para o Mojarra é definido como com.sun.faces.context.ExceptionHandlerImpl . Esta implementação basicamente irá relançar qualquer exceção, exceto quando se trata de um AbortProcessingException, que é registrado. Os ActionListeners envolvem a exceção que é lançada pelo código do cliente em tal AbortProcessingException, o que explica por que eles sempre são registrados.

Este ExceptionHandler pode ser substituído no faces-config.xml com uma implementação customizada:

  com.foo.myExceptionHandler  

Em vez de escutar globalmente, um único bean também pode ouvir esses events. O seguinte é uma prova de conceito disto:

 @ManagedBean @RequestScoped public class MyBean { public void actionMethod(ActionEvent event) { FacesContext.getCurrentInstance().getApplication().subscribeToEvent(ExceptionQueuedEvent.class, new SystemEventListener() { @Override public void processEvent(SystemEvent event) throws AbortProcessingException { ExceptionQueuedEventContext content = (ExceptionQueuedEventContext)event.getSource(); throw new RuntimeException(content.getException()); } @Override public boolean isListenerForSource(Object source) { return true; } }); throw new RuntimeException("test"); } } 

(note que não se deve normalmente codificar os ouvintes, isto é apenas para fins de demonstração!)

Chamando isso de um Facelet assim:

        

Resultará em uma página de erro sendo exibida.

O ActionListener é acionado primeiro, com uma opção para modificar a resposta, antes de chamar a Ação e determinar o local da próxima página.

Se você tem vários botões na mesma página que devem ir para o mesmo local, mas fazem coisas um pouco diferentes, você pode usar a mesma Ação para cada botão, mas usar um ActionListener diferente para lidar com uma funcionalidade ligeiramente diferente.

Aqui está um link que descreve o relacionamento:

http://www.java-samples.com/showtutorial.php?tutorialid=605