wake-up-neo.com

Unterschiede zwischen action und actionListener

Was ist der Unterschied zwischen action und actionListener, und wann sollte ich action im Vergleich zu actionListener verwenden?

366
Murat Güzel

actionListener

Verwenden Sie actionListenername__, wenn Sie einen Hook haben möchten, bevor die echte Geschäftsaktion ausgeführt wird, z. um es zu protokollieren und/oder eine zusätzliche Eigenschaft festzulegen (durch <f:setPropertyActionListener> ) und/oder um auf die Komponente zuzugreifen, die die Aktion aufgerufen hat (die durch verfügbar ist) ActionEventname __ argument). Rein zu Vorbereitungszwecken, bevor die eigentliche Geschäftsaktion aufgerufen wird.

Die Methode actionListenerhat standardmäßig die folgende Signatur:

_import javax.faces.event.ActionEvent;
// ...

public void actionListener(ActionEvent event) {
    // ...
}
_

Und es soll wie folgt deklariert werden, ohne Klammern für die Methode:

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

Beachten Sie, dass Sie zusätzliche Argumente von EL 2.2 nicht übergeben können. Sie können jedoch das Argument ActionEventinsgesamt überschreiben, indem Sie benutzerdefinierte Argumente übergeben und angeben. Folgende Beispiele sind gültig:

_<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) {}
_

Beachten Sie die Bedeutung der Klammern im argumentlosen Methodenausdruck. Wenn sie nicht vorhanden wären, würde JSF immer noch eine Methode mit dem Argument ActionEventerwarten.

Wenn Sie mit EL 2.2+ arbeiten, können Sie über _<f:actionListener binding>_ mehrere Aktionslistener-Methoden deklarieren.

_<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() {}
_

Beachten Sie die Bedeutung der Klammern im Attribut bindingname__. Wenn sie nicht vorhanden wären, würde EL verwirrend ein _javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean_ auslösen, da das Attribut bindingstandardmäßig als Wertausdruck und nicht als Methodenausdruck interpretiert wird. Durch transparentes Hinzufügen von Klammern im EL 2.2+ -Stil wird ein Werteausdruck in einen Methodenausdruck umgewandelt. Siehe auch a.o. Warum kann ich <f: actionListener> an eine beliebige Methode binden, wenn diese nicht von JSF unterstützt wird?


aktion

Verwenden Sie actionname__, wenn Sie eine Geschäftsaktion ausführen und gegebenenfalls die Navigation verwalten möchten. Die Methode actionkann (muss also nicht) einen Stringnamen__ zurückgeben, der als Navigationsfallergebnis (die Zielansicht) verwendet wird. Mit dem Rückgabewert nulloder voidwird zur selben Seite zurückgekehrt und der aktuelle Anzeigebereich beibehalten. Ein Rückgabewert einer leeren Zeichenfolge oder derselben Ansichts-ID kehrt ebenfalls zur selben Seite zurück, erstellt jedoch den Ansichtsbereich neu und zerstört somit alle derzeit aktiven View-Scoped-Beans und erstellt sie gegebenenfalls neu.

Die Methode actionkann ein beliebiges gültiges MethodExpressionNAME _ sein, auch solche, die EL 2.2-Argumente wie die folgenden verwenden:

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

Mit dieser Methode:

_public void edit(Item item) {
    // ...
}
_

Beachten Sie, dass Sie, wenn Ihre Aktionsmethode nur eine Zeichenfolge zurückgibt, auch genau diese Zeichenfolge im Attribut actionangeben können. Das ist also total ungeschickt:

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

Mit dieser sinnlosen Methode wird eine fest codierte Zeichenfolge zurückgegeben:

_public String goToNextpage() {
    return "nextpage";
}
_

Fügen Sie stattdessen die fest codierte Zeichenfolge direkt in das Attribut ein:

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

Bitte beachten Sie, dass dies wiederum auf ein schlechtes Design hinweist: Navigation per POST. Dies ist weder benutzer- noch SEO-freundlich. Dies alles wird in Wann sollte ich h: outputLink anstelle von h: commandLink verwenden? erklärt und soll wie folgt gelöst werden

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

Siehe auch Wie navigiert man in JSF? Wie spiegelt die URL die aktuelle Seite (und nicht die vorherige) wider .


f: Ajax Listener

Seit JSF 2.x gibt es einen dritten Weg, den _<f:ajax listener>_.

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

Die Methode ajaxListenerhat standardmäßig die folgende Signatur:

_import javax.faces.event.AjaxBehaviorEvent;
// ...

public void ajaxListener(AjaxBehaviorEvent event) {
    // ...
}
_

In Mojarra ist das Argument AjaxBehaviorEventoptional (siehe unten).

_public void ajaxListener() {
    // ...
}
_

In MyFaces würde es jedoch einen MethodNotFoundExceptionauslösen. Das Folgende funktioniert in beiden JSF-Implementierungen, wenn Sie das Argument weglassen möchten.

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

Ajax-Listener sind für Befehlskomponenten nicht wirklich nützlich. Sie sind bei der Eingabe und Auswahl von Komponenten _<h:inputXxx>_/_<h:selectXxx>_ nützlicher. Halten Sie sich in Befehlskomponenten einfach an actionund/oder actionListenername__, um die Übersichtlichkeit und den selbstdokumentierenden Code zu verbessern. Wie actionListenerunterstützt auch _f:ajax listener_ die Rückgabe eines Navigationsergebnisses nicht.

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

Erläuterungen zu den Attributen executeund renderfinden Sie unter Grundlegendes zu PrimeFaces-Prozessen/-Updates und JSF f: Ajax Execute/Render-Attributen .


Aufrufreihenfolge

Die actionListenername__s werden immer aufgerufen vor der actionin derselben Reihenfolge, in der sie in der Ansicht deklariert und an die Komponente angehängt wurden. Der _f:ajax listener_ wird immer aufgerufen vor jedem Aktionslistener. Also das folgende Beispiel:

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

Ruft die Methoden in der folgenden Reihenfolge auf:

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

Ausnahmebehandlung

Der actionListenerunterstützt eine spezielle Ausnahme: AbortProcessingExceptionNAME _ . Wenn diese Ausnahme von einer actionListenername__-Methode ausgelöst wird, überspringt JSF alle verbleibenden Aktionslistener und die Aktionsmethode und fährt mit dem direkten Rendern der Antwort fort. Sie werden keine Fehler-/Ausnahmeseite sehen, JSF wird sie jedoch protokollieren. Dies wird implizit auch immer dann durchgeführt, wenn eine andere Ausnahme von einem actionListenergeworfen wird. Wenn Sie also beabsichtigen, die Seite aufgrund einer Geschäftsausnahme durch eine Fehlerseite zu blockieren, sollten Sie den Job auf jeden Fall in der Methode actionausführen.

Wenn der einzige Grund für die Verwendung einer actionListenerdarin besteht, dass eine voidname__-Methode zur selben Seite zurückkehrt, ist dies eine schlechte. Die actionname__-Methoden können auch voidzurückgeben, im Gegensatz zu dem, was einige IDEs über die EL-Validierung vermuten lassen. Beachten Sie, dass die Beispiele PrimeFaces showcase überall mit dieser Art von actionListenername__s übersät sind. Das ist in der Tat falsch. Verwenden Sie dies nicht als Entschuldigung, um dies auch selbst zu tun.

Bei Ajax-Anforderungen wird jedoch ein spezieller Ausnahmebehandler benötigt. Dies ist unabhängig davon, ob Sie das Attribut listenervon _<f:ajax>_ verwenden oder nicht. Zur Erläuterung und als Beispiel gehen Sie zu Ausnahmebehandlung in JSF-Ajax-Anforderungen .

562
BalusC

Wie BalusC angedeutet hat, schluckt actionListener standardmäßig Ausnahmen, aber in JSF 2.0 steckt noch ein bisschen mehr dahinter. Es verschluckt und protokolliert nämlich nicht nur, sondern tatsächlich veröffentlicht die Ausnahme.

Dies geschieht durch einen Aufruf wie folgt:

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

Der Standard-Listener für dieses Ereignis ist ExceptionHandler, der für Mojarra auf com.Sun.faces.context.ExceptionHandlerImpl festgelegt ist. Diese Implementierung löst grundsätzlich alle Ausnahmen erneut aus, es sei denn, es handelt sich um eine protokollierte AbortProcessingException. ActionListener verpacken die vom Clientcode ausgelöste Ausnahme in eine solche AbortProcessingException, die erklärt, warum diese immer protokolliert werden.

Dieses ExceptionHandler kann jedoch in faces-config.xml durch eine benutzerdefinierte Implementierung ersetzt werden:

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

Anstatt global zu lauschen, kann eine einzelne Bean diese Ereignisse auch abhören. Das Folgende ist ein Proof of Concept dafür:

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

}

(Achtung, so sollte man normalerweise keine Listener codieren, dies dient nur zu Demonstrationszwecken!)

Aufruf von einem Facelet wie folgt:

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

Wird eine Fehlerseite angezeigt.

45
Arjan Tijms

ActionListener wird zuerst ausgelöst, mit einer Option zum Ändern der Antwort, bevor Action aufgerufen wird und die Position der nächsten Seite bestimmt.

Wenn auf derselben Seite mehrere Schaltflächen vorhanden sind, die zum selben Ort gehen, jedoch geringfügig unterschiedliche Aktionen ausführen sollen, können Sie für jede Schaltfläche dieselbe Aktion verwenden, jedoch einen anderen ActionListener verwenden, um leicht unterschiedliche Funktionen zu verwenden.

Hier ist ein Link, der die Beziehung beschreibt:

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

39
Erick Robertson

TL; DR :

Die ActionListeners (es kann mehrere geben) werden in der Reihenfolge ausgeführt, in der sie registriert wurden, BEVOR die action 

Lange Antwort :

Ein Geschäft action ruft normalerweise einen EJB-Service auf und legt gegebenenfalls das Endergebnis fest und/oder navigiert zu einer anderen Ansicht Wenn dies nicht der Fall ist, ist eine actionListener geeigneter, dh wenn der Benutzer mit den Komponenten interagiert. B. h:commandButton oder h:link können sie durch Übergeben des Namens der verwalteten Bean-Methode im actionListener-Attribut einer UI-Komponente oder zur Implementierung einer ActionListener-Schnittstelle und Übergabe des Implementierungsklassennamens an das actionListener-Attribut einer UI-Komponente behandelt werden.

0
Yehuda Schwartz