actionListner 属性相对于 h:commandButton 中的 action 属性有什么技术优势吗?

Is there any technical advantage of actionListner attribute over action attribute in h:commandButton?

在核心 JSF 书籍(第 312 页)中,作者谈到了这一点:

Note that an action alone cannot implement that behavior—an action can navigate to the appropriate page, but it cannot determine the appropriate page because it knows nothing about the image button in the user interface or the mouse click.

下面是该示例中使用的 index.xhtml 和 ManagedBean:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"  
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
   <h:head>
      <h:outputStylesheet library="css" name="styles.css"/>
      <title>#{msgs.indexWindowTitle}</title>
   </h:head>

   <h:body>
      <span class="instructions">#{msgs.instructions}</span>
      <h:form>
         <h:commandButton image="/resources/images/mountrushmore.jpg" 
                          styleClass="imageButton"
                          actionListener="#{rushmore.handleMouseClick}"
                          action="#{rushmore.navigate}"/>
      </h:form>
   </h:body>
</html>

托管 Bean:

package com.corejsf;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.Map;

import javax.faces.bean.ManagedBean; 
   // or import javax.inject.Named;
import javax.enterprise.context.RequestScoped; 
   // or import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

@ManagedBean // or @Named
@RequestScoped
public class Rushmore {
   private String outcome = null;
   private Rectangle washingtonRect = new Rectangle(70, 30, 40, 40);
   private Rectangle jeffersonRect = new Rectangle(115, 45, 40, 40);
   private Rectangle rooseveltRect = new Rectangle(135, 65, 40, 40);
   private Rectangle lincolnRect = new Rectangle(175, 62, 40, 40);

   public void handleMouseClick(ActionEvent e) {
      FacesContext context = FacesContext.getCurrentInstance();
      String clientId = e.getComponent().getClientId(context);
      Map<String, String> requestParams 
         = context.getExternalContext().getRequestParameterMap();

      int x = new Integer((String) requestParams.get(clientId + ".x")).intValue();
      int y = new Integer((String) requestParams.get(clientId + ".y")).intValue();

      outcome = null;

      if (washingtonRect.contains(new Point(x, y)))
         outcome = "washington";

      if (jeffersonRect.contains(new Point(x, y)))
         outcome = "jefferson";

      if (rooseveltRect.contains(new Point(x, y)))
         outcome = "roosevelt";

      if (lincolnRect.contains(new Point(x, y)))
         outcome = "lincoln";
   }

   public String navigate() {
      return outcome;
   }
}

我不明白作者为什么这么说,因为我试图只使用 action 属性并绑定到 commandButton,所以我修改了 index.xhtml 和托管 bean:

新建index.xhtml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"  
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
   <h:head>
      <h:outputStylesheet library="css" name="styles.css"/>
      <title>#{msgs.indexWindowTitle}</title>
   </h:head>

   <h:body>
      <span class="instructions">#{msgs.instructions}</span>
      <h:form>
         <h:commandButton image="/resources/images/mountrushmore.jpg" id="commandButton" binding="#{rushmore.command}"
                          styleClass="imageButton" ac
                          action="#{rushmore.navigate}"/>
      </h:form>
      <ui:debug></ui:debug>
   </h:body>
</html>

新托管 Bean:

package com.corejsf;

import java.awt.Point;
import java.awt.Rectangle;
import java.util.Map;

import javax.faces.bean.ManagedBean;
// or import javax.inject.Named;
import javax.faces.bean.RequestScoped;
// or import javax.faces.bean.RequestScoped;
import javax.faces.component.UICommand;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

@ManagedBean
// or @Named
@RequestScoped
public class Rushmore {
    private String outcome = null;
    private Rectangle washingtonRect = new Rectangle(70, 30, 40, 40);
    private Rectangle jeffersonRect = new Rectangle(115, 45, 40, 40);
    private Rectangle rooseveltRect = new Rectangle(135, 65, 40, 40);
    private Rectangle lincolnRect = new Rectangle(175, 62, 40, 40);
    private UICommand command;

    public String navigate() {
        FacesContext context = FacesContext.getCurrentInstance();
        String clientId = command.getClientId(context);
        Map<String, String> requestParams = context.getExternalContext()
                .getRequestParameterMap();

        int x = new Integer((String) requestParams.get(clientId + ".x"))
                .intValue();
        int y = new Integer((String) requestParams.get(clientId + ".y"))
                .intValue();

        outcome = null;

        if (washingtonRect.contains(new Point(x, y)))
            outcome = "washington";

        if (jeffersonRect.contains(new Point(x, y)))
            outcome = "jefferson";

        if (rooseveltRect.contains(new Point(x, y)))
            outcome = "roosevelt";

        if (lincolnRect.contains(new Point(x, y)))
            outcome = "lincoln";

        return outcome;
    }

    public UICommand getCommand() {
        return command;
    }

    public void setCommand(UICommand command) {
        this.command = command;
    }
}

所以应用程序完全按照它应该的方式工作。 我的问题是在该示例中使用 actionListner 而不是仅使用 action 的技术优势(据我所知,从概念的角度来看,我们必须将业务逻辑与用户界面逻辑分开)属性?

这个问题在评论里已经回答了,所以我复制一下BalusC的回复:

Nope. It's purely for demonstration purposes. It would only be beneficial if the outcome part was determined in action instead of in actionListener. Right now the handleMouseClick() action listener isn't reusable on different actions expecting a different outcome