Answers:
使用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+样式括号会透明地将值表达式转换为方法表达式。另请参见ao 如果JSF不支持<f:actionListener>为何可以将它绑定到任意方法?
使用action
,如果你想执行业务操作,并在必要时处理导航。该action
方法可以(因此不是必须)返回String
,它将用作导航案例的结果(目标视图)。的返回值null
或void
将使其返回同一页面并使当前视图范围保持活动状态。空字符串或相同视图ID的返回值也将返回到同一页面,但是将重新创建视图范围,从而销毁任何当前活动的视图范围的bean,并在适用时重新创建它们。
该action
方法可以是任何有效方法MethodExpression
,也可以是使用EL 2.2参数的方法,例如:
<h:commandXxx value="submit" action="#{bean.edit(item)}" />
使用此方法:
public void edit(Item item) {
// ...
}
请注意,当您的操作方法仅返回一个字符串时,您也可以只在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反映当前页面(而不是上一页)。
从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>
有关属性的解释execute
和说明render
,请前往了解PrimeFaces流程/更新和JSF f:ajax执行/渲染属性。
所述actionListener
s的总是调用之前的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>
将按以下顺序调用方法:
Bean#ajaxListener()
Bean#actionListener()
ActionListenerType#processAction()
Bean#actionListenerBinding()
Bean#setProperty()
Bean#action()
在actionListener
支持一个特殊的例外:AbortProcessingException
。如果从actionListener
方法抛出此异常,那么JSF将跳过所有剩余的动作侦听器和该动作方法,并继续直接呈现响应。您将不会看到错误/异常页面,但是JSF会将其记录下来。每当从抛出任何其他异常时,也将隐式完成此操作actionListener
。因此,如果由于业务异常而打算通过错误页面来阻止该页面,那么您肯定应该在该action
方法中执行该作业。
如果使用an的唯一原因actionListener
是让一种void
方法返回到同一页面,那么那是一个不好的选择。与某些IDE通过EL验证让您相信的相反,这些action
方法也可以完美地返回void
。请注意,PrimeFaces展示示例actionListener
在所有地方到处都是此类。这确实是错误的。不要以此为借口自己做。
但是,在ajax请求中,需要特殊的异常处理程序。这与是否使用listener
of属性<f:ajax>
无关。有关说明和示例,请转到JSF ajax request中的异常处理。
actionListener
,但仍然没有使它成为一个很好的借口,滥用actionListener
的商业行为。
action
相对应。actionListener
是次要的东西。只是想澄清一下,actionListener
如果需要可以传播s的异常;)
actionListener
属性中使用时可以自由选择,并且也必须public
如此。processAction
仅当使用时<f:actionListener type>
,该名称才是必需的,这是因为该类型必须实现ActionListener
具有确切定义该方法名称的processAction
接口。
<f:ajax>
,在使用命令组件的情况下,您还是希望将action
属性用于业务操作。例如<h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>
。
正如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。ActionListeners将客户端代码引发的异常包装在这样的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>
将导致显示错误页面。
在调用Action并确定下一页的位置之前,将首先触发ActionListener并提供修改响应的选项。
如果您在同一页面上有多个按钮,这些按钮应该移至相同的位置,但执行的操作略有不同,则可以为每个按钮使用相同的Action,但使用不同的ActionListener处理稍微不同的功能。
这是描述关系的链接: