action和actionListener的区别

actionactionListener之间的区别是什么,什么时候应该使用actionactionListener?

284303 次浏览

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

如果你在同一个页面上有多个按钮,它们应该指向相同的位置,但做的事情略有不同,你可以为每个按钮使用相同的Action,但使用不同的ActionListener来处理略有不同的功能。

下面是一个描述这种关系的链接:

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

actionListener

使用actionListener如果你想让一个钩子之前真正的业务操作被执行,例如,记录它,和/或设置一个额外的属性(通过<f:setPropertyActionListener>),和/或访问调用该操作的组件(通过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) {}

请注意括号在无参数方法表达式中的重要性。如果它们不存在,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属性默认被解释为值表达式,而不是方法表达式。添加EL 2.2+样式括号可以透明地将值表达式转换为方法表达式。另见a.o. 为什么我能够绑定<f:actionListener>如果JSF不支持任意方法?


行动

使用action如果你想执行一个业务操作,并在必要时处理导航。action方法可以(因此,不是必须)返回一个String,它将被用作导航案例的结果(目标视图)。nullvoid的返回值将让它返回到同一页,并保持当前视图范围为活动状态。空字符串或相同视图ID的返回值也将返回到同一页,但将重新创建视图作用域,从而销毁当前活动的视图作用域bean,如果适用,重新创建它们。

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

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

用这个方法:

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

注意,当你的action方法只返回一个字符串时,你也可以在action属性中精确地指定该字符串。因此,这完全是笨拙的:

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

使用这个无意义的方法返回一个硬编码的字符串:

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

相反,只需将硬编码的字符串直接放在属性中:

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

请注意,这反过来表明一个糟糕的设计:通过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侦听器在命令组件上并不是很有用。它们在输入和选择组件<h:inputXxx>/<h:selectXxx>上更有用。在命令组件中,只要坚持使用action和/或actionListener即可,以获得更清晰和更好的自文档代码。而且,像actionListener一样,f:ajax listener不支持返回导航结果。

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

有关executerender属性的解释,请转到理解PrimeFaces处理/更新和JSF f:ajax执行/呈现属性


调用顺序

__abc0总是按照在视图中声明并附加到组件的相同顺序调用之前actionf: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()

异常处理

actionListener支持一个特殊异常:AbortProcessingException。如果此异常是从actionListener方法抛出的,那么JSF将跳过所有剩余的操作侦听器和操作方法,并继续直接呈现响应。您不会看到错误/异常页面,但是JSF会记录它。当从actionListener抛出任何其他异常时,这也将隐式地完成。因此,如果你打算通过业务异常导致的错误页来阻塞页面,那么你肯定应该在action方法中执行这项工作。

如果使用actionListener的唯一原因是让void方法返回到同一页,那么这是一个糟糕的方法。action方法也可以完美地返回void,这与一些ide通过EL验证让你相信的相反。注意,PrimeFaces展示的例子中到处都是这种__abc0。这的确是错误的。不要以此为借口自己也这么做。

然而,在ajax请求中,需要一个特殊的异常处理程序。这与你是否使用<f:ajax>listener属性无关。有关解释和示例,请转到JSF ajax请求中的异常处理

正如BalusC所指出的,actionListener默认情况下会吞咽异常,但在JSF 2.0中还有更多的东西。也就是说,它不仅仅是燕子和原木,实际上发布是例外。

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

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

这个事件的默认监听器是ExceptionHandler, Mojarra的监听器设置为com.sun.faces.context.ExceptionHandlerImpl。这个实现基本上会重新抛出任何异常,除非它涉及AbortProcessingException,该异常会被记录下来。actionlistener将客户端代码抛出的异常包装在这样一个AbortProcessingException中,这解释了为什么这些总是被记录下来。

这个ExceptionHandler可以在faces-config.xml中使用自定义实现替换:

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

单个bean也可以侦听这些事件,而不是全局侦听。以下是对这一概念的证明:

@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>

将导致显示错误页面。

博士TL;:

__abc0(可以有多个)按照它们在action之前注册的顺序执行

长回答:

业务action通常调用EJB服务,如果需要,还设置最终结果和/或导航到不同的视图 如果这不是你正在做的事情,actionListener更合适,即当用户与组件交互时,例如h:commandButtonh:link,它们可以通过在UI组件的actionListener属性中传递托管bean方法的名称来处理,或者实现ActionListener接口并将实现类名传递给UI组件的actionListener属性