action和actionListener之间的区别

actionactionListener之间有什么区别,什么时候应该使用actionactionListener

的ActionListener

使用actionListener如果你想在真正的业务操作执行之前有一个钩子,例如logging它,和/或设置一个额外的属性(通过<f:setPropertyActionListener> ),和/或有权访问调用action( ActionEvent参数可用)。 所以,纯粹是为了在真正的商业行为被调用之前的准备目的。

actionListener方法默认具有以下签名:

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

它应该被声明如下,没有任何方法的括号:

 <h:commandXxx ... actionListener="#{bean.actionListener}" /> 

请注意,EL 2.2不能传递额外的参数。 但是,您可以通过传递和指定自定义参数来完全覆盖ActionEvent参数。 以下示例是有效的:

 <h:commandXxx ... actionListener="#{bean.methodWithoutArguments()}" /> <h:commandXxx ... actionListener="#{bean.methodWithOneArgument(arg1)}" /> <h:commandXxx ... actionListener="#{bean.methodWithTwoArguments(arg1, arg2)}" /> 
 public void methodWithoutArguments() {} public void methodWithOneArgument(Object arg1) {} public void methodWithTwoArguments(Object arg1, Object arg2) {} 

请注意无参数方法expression式中括号的重要性。 如果他们缺席,JSF仍然会期待一个带有ActionEvent参数的方法。

如果你使用EL 2.2+,那么你可以通过<f:actionListener binding>声明多个动作监听器方法。

 <h:commandXxx ... actionListener="#{bean.actionListener1}"> <f:actionListener binding="#{bean.actionListener2()}" /> <f:actionListener binding="#{bean.actionListener3()}" /> </h:commandXxx> 
 public void actionListener1(ActionEvent event) {} public void actionListener2() {} public void actionListener3() {} 

请注意binding属性中括号的重要性。 如果他们缺席,EL会混淆地抛出一个javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean ,因为binding属性默认被解释为值expression式,而不是方法expression式。 添加EL 2.2+样式圆括号可以将值expression式转换为方法expression式。 另请参见ao 为什么我能够将<f:actionListener>绑定到任意方法(如果它不受JSF支持)?


行动

如果您要执行业务操作并在必要时处理导航,请使用actionaction方法可以(因此不必)返回将被用作导航事件结果的String (目标视图)。 nullvoid返回值将使其返回到同一页面并保持当前视图范围处于活动状态。 空string的返回值或相同的视图ID也将返回到同一页面,但重新创build视图范围,从而销毁当前活动的视图范围的bean,并在适用的情况下重新创build它们。

action方法可以是任何有效的MethodExpression ,也可以是使用EL 2.2参数的方法,例如:

 <h:commandXxx value="submit" action="#{bean.edit(item)}" /> 

用这个方法:

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

请注意,如果您的操作方法仅返回一个string,那么您也可以只在action属性中指定该string。 因此,这是完全笨拙的:

 <h:commandLink value="Go to next page" action="#{bean.goToNextpage}" /> 

有了这个毫无意义的方法返回一个硬编码的string:

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

相反,只需将该硬编码string直接放在属性中:

 <h:commandLink value="Go to next page" action="nextpage" /> 

请注意,这反过来表明一个糟糕的devise:通过POST导航。 这不是用户也不是SEO友好的。 这一切都解释了什么时候应该使用h:outputLink而不是h:commandLink? 并应该被解决为

 <h:link value="Go to next page" outcome="nextpage" /> 

另请参见如何在JSF中导航? 如何使URL反映当前页面(而不是以前的页面) 。


f:ajax监听器

由于JSF 2.x还有第三种方式, <f:ajax listener>

 <h:commandXxx ...> <f:ajax listener="#{bean.ajaxListener}" /> </h:commandXxx> 

ajaxListener方法默认具有以下签名:

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

在Mojarra中, AjaxBehaviorEvent参数是可选的,下面的工作很好。

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

但在MyFaces中,它会抛出一个MethodNotFoundException 。 下面在两个JSF实现中都可以省略参数。

 <h:commandXxx ...> <f:ajax execute="@form" listener="#{bean.ajaxListener()}" render="@form" /> </h:commandXxx> 

Ajax监听器在命令组件上并不是很有用。 它们对于input和select组件<h:inputXxx> / <h:selectXxx> 。 在命令组件中,为了清晰和更好的自我logging代码,只需坚持action和/或actionListener 。 而且,和actionListener一样, f:ajax listener器也不支持返回导航结果。

 <h:commandXxx ... action="#{bean.action}"> <f:ajax execute="@form" render="@form" /> </h:commandXxx> 

有关executerender属性的说明,请前往了解PrimeFaces进程/更新和JSF f:ajax执行/呈现属性 。


调用顺序

actionListener总是 action 之前按照它们在视图中声明并附加到组件的顺序调用。 f:ajax listener器总是任何监听器之前被调用。 所以,下面的例子:

 <h:commandButton value="submit" actionListener="#{bean.actionListener}" action="#{bean.action}"> <f:actionListener type="com.example.ActionListenerType" /> <f:actionListener binding="#{bean.actionListenerBinding()}" /> <f:setPropertyActionListener target="#{bean.property}" value="some" /> <f:ajax listener="#{bean.ajaxListener}" /> </h:commandButton> 

将按以下顺序调用这些方法:

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

exception处理

actionListener支持一个特殊的exception: AbortProcessingException 。 如果这个exception是从actionListener方法抛出的,那么JSF将会跳过所有剩下的动作侦听器和action方法,并直接渲染响应。 您将不会看到错误/exception页面,但是JSF会logging它。 当从actionListener抛出任何其他exception时,这也将隐式地完成。 所以,如果你打算通过一个错误页面来阻止页面作为一个业务exception的结果,那么你一定要在action方法中执行这个工作。

如果使用actionListener的唯一理由是让一个void方法返回到同一个页面,那么这是一个糟糕的方法。 action方法完全可以返回void ,相反,有些IDE通过ELvalidation让你相信。 请注意, PrimeFaces展示示例遍布所有地方的这种actionListener 。 这确实是错误的。 不要以此为借口自己去做。

但是,在ajax请求中,需要一个特殊的exception处理程序。 这与您是否使用<f:ajax> listener属性无关。 有关说明和示例,请转到JSF ajax请求中的exception处理 。

正如BalusC所指出的那样, actionListener在默认情况下会吞噬exception,但在JSF 2.0中还有一点更多。 也就是说,它不只是吞下并login,而是实际发布exception。

这是通过这样的调用发生的:

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

这个事件的默认监听器是ExceptionHandler ,它为Mojarra设置了com.sun.faces.context.ExceptionHandlerImpl 。 这个实现基本上会重新抛出任何exception,除非它涉及一个logging的AbortProcessingException。 ActionListeners将由客户端代码抛出的exception包装在AbortProcessingException中,这解释了为什么会始终logging这些exception。

这个ExceptionHandler可以在faces-config.xml中用一个自定义的实现来replace:

 <exception-handlerfactory> com.foo.myExceptionHandler </exception-handlerfactory> 

而不是全局监听,一个bean也可以监听这些事件。 以下是这个概念的一个certificate:

 @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"); } } 

(注意,这不是通常的代码监听器,这只是为了演示目的!)

从Facelet调用这样的:

 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:body> <h:form> <h:commandButton value="test" actionListener="#{myBean.actionMethod}"/> </h:form> </h:body> </html> 

将导致显示错误页面。

在Action被调用并确定下一页的位置之前,ActionListener首先被触发,并有一个修改响应的选项。

如果在同一个页面上有多个button应该放在同一个地方,但做了些微不同的事情,则可以对每个button使用相同的Action,但使用不同的ActionListener来处理稍微不同的function。

以下是描述关系的链接:

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