action和actionListener之间的差异

Mur*_*zel 385 jsf action actionlistener jsf-2

是什么区别actionactionListener,什么时候应该使用actionactionListener

Bal*_*usC 571

的ActionListener

使用actionListener,如果你想有一个钩子之前得到执行的实际业务操作,如记录它,和/或设置附加属性(通过<f:setPropertyActionListener>),和/或访问其调用的动作(这是可以由组件ActionEvent参数).因此,在实际业务操作被调用之前,纯粹是为了准备目的.

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

import javax.faces.event.ActionEvent;
// ...

public void actionListener(ActionEvent event) {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

<h:commandXxx ... actionListener="#{bean.actionListener}" />
Run Code Online (Sandbox Code Playgroud)

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

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

请注意无参数方法表达式中括号的重要性.如果它们不存在,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>
Run Code Online (Sandbox Code Playgroud)
public void actionListener1(ActionEvent event) {}
public void actionListener2() {}
public void actionListener3() {}
Run Code Online (Sandbox Code Playgroud)

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


行动

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

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

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

用这种方法:

public void edit(Item item) {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

<h:commandLink value="Go to next page" action="#{bean.goToNextpage}" />
Run Code Online (Sandbox Code Playgroud)

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

public String goToNextpage() {
    return "nextpage";
}
Run Code Online (Sandbox Code Playgroud)

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

<h:commandLink value="Go to next page" action="nextpage" />
Run Code Online (Sandbox Code Playgroud)

请注意,这反过来表明设计不好:通过POST导航.这不是用户也不是SEO友好.这一切都在我何时应该使用h:outputLink而不是h:commandLink?应该被解决为

<h:link value="Go to next page" outcome="nextpage" />
Run Code Online (Sandbox Code Playgroud)

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


f:ajax监听器

从JSF 2.x开始,第三种方式就是<f:ajax listener>.

<h:commandXxx ...>
    <f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
Run Code Online (Sandbox Code Playgroud)

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

import javax.faces.event.AjaxBehaviorEvent;
// ...

public void ajaxListener(AjaxBehaviorEvent event) {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

在Mojarra中,AjaxBehaviorEvent参数是可选的,下面的效果很好.

public void ajaxListener() {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

但在MyFaces中,它会抛出一个MethodNotFoundException.当你想省略参数时,下面的两个JSF实现都适用.

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

Ajax侦听器在命令组件上并不真正有用.它们对输入和选择组件<h:inputXxx>/ 更有用<h:selectXxx>.在命令组件中,只需坚持action和/或actionListener清晰,以及更好的自我记录代码.而且,例如actionListener,f:ajax listener不支持返回导航结果.

<h:commandXxx ... action="#{bean.action}">
    <f:ajax execute="@form" render="@form" />
</h:commandXxx>
Run Code Online (Sandbox Code Playgroud)

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


调用顺序

所述actionListeners的总是调用之前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>
Run Code Online (Sandbox Code Playgroud)

将按以下顺序调用方法:

  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方法中执行该作业.

如果使用a的唯一原因actionListener是让void方法返回到同一页面,那么这是一个糟糕的方法.这些action方法可以完美地返回void,相反,某些IDE让您通过EL验证可以相信.请注意,PrimeFaces展示的例子actionListener在所有地方都充满了这种情况.这确实是错的.不要以此为借口自己也这样做.

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

  • @arjan:你是对的,JSF 2.0允许你改变`actionListener`抛出的异常的默认处理,但这仍然不能成为滥用`actionListener` for**business**action的好借口. (3认同)
  • @Kawy:在`actionListener`属性中使用时,方法名称可以自由选择,并且它也必须是`public`.只有当你使用`<f:actionListener type>`时,`processAction`名称才是必需的,因为该类型必须实现`ActionListener`接口,该接口具有确切的方法名称`processAction`. (2认同)
  • @Muhammed:在所有常规动作侦听器之前调用ajax动作侦听器.请注意,即使使用`<f:ajax>`,如果命令组件更喜欢使用`action`属性进行业务操作.例如`<h:commandButton action ="#{bean.businessAction}"> <f:ajax /> </ h:commandButton>`. (2认同)

Arj*_*jms 47

正如BalusC指出的那样,actionListener默认情况下吞下异常,但在JSF 2.0中还有更多内容.也就是说,它不仅仅是吞下和记录,而是实际发布异常.

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

context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,                                                          
    new ExceptionQueuedEventContext(context, exception, source, phaseId)
);
Run Code Online (Sandbox Code Playgroud)

此事件的默认侦听器是ExceptionHandlerMojarra设置的默认侦听器com.sun.faces.context.ExceptionHandlerImpl.这个实现基本上会重新抛出任何异常,除非它涉及记录的AbortProcessingException.ActionListeners将客户端代码抛出的异常包装在这样的AbortProcessingException中,这解释了为什么始终记录这些异常.

ExceptionHandler可以然而faces-config.xml中有一个自定义实现替换:

<exception-handlerfactory>
   com.foo.myExceptionHandler
</exception-handlerfactory>
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

(注意,这不是人们通常应该如何编写监听器的代码,这仅用于演示目的!)

从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>
Run Code Online (Sandbox Code Playgroud)

将导致显示错误页面.


Eri*_*son 43

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

如果同一页面上有多个按钮应该转到同一个地方但稍微不同的东西,则可以对每个按钮使用相同的Action,但使用不同的ActionListener来处理稍微不同的功能.

以下是描述关系的链接:

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

  • 再加上一个,粗体字母几乎说明了一切. (3认同)

归档时间:

查看次数:

247615 次

最近记录:

6 年,9 月 前