412 Stimmen

Unterschiede zwischen action und actionListener

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

618voto

BalusC Punkte 1034465

ActionListener

Verwenden Sie actionListener wenn Sie einen Haken haben wollen antes de die eigentliche Geschäftsaktion ausgeführt wird, z.B. um sie zu protokollieren, und/oder um eine zusätzliche Eigenschaft zu setzen (durch <f:setPropertyActionListener> ), und/oder um Zugang zu der Komponente zu haben, die die Aktion aufgerufen hat (verfügbar über ActionEvent Argument). Es dient also lediglich der Vorbereitung, bevor die eigentliche Geschäftsaktion aufgerufen wird.

En actionListener Methode hat standardmäßig die folgende Signatur:

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

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

Und es soll wie folgt deklariert werden, ohne Klammern:

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

Beachten Sie, dass Sie nicht zusätzlich Argumente von EL 2.2. Sie können jedoch die ActionEvent Argument insgesamt durch Übergabe und Angabe von benutzerdefinierten Argumenten. Die folgenden 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 in dem argumentlosen Methodenausdruck. Würden sie fehlen, würde JSF immer noch eine Methode mit ActionEvent Argument.

Wenn Sie EL 2.2+ verwenden, können Sie mehrere Action-Listener-Methoden über <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() {}

Beachten Sie die Bedeutung der Klammern in der binding zuordnen. Wenn sie fehlten, würde EL verwirrenderweise ein javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean denn die binding Attribut wird standardmäßig als Wertausdruck und nicht als Methodenausdruck interpretiert. Das Hinzufügen von Klammern im Stil von EL 2.2+ verwandelt einen Wertausdruck transparent in einen Methodenausdruck. Siehe auch a.o. Warum kann ich <f:actionListener> an eine beliebige Methode binden, wenn sie von JSF nicht unterstützt wird?


Aktion

Verwenden Sie action wenn Sie eine geschäftliche Aktion ausführen und gegebenenfalls die Navigation steuern wollen. Die action Methode kann (muss also nicht) eine String die als Navigationsfallergebnis (die Zielansicht) verwendet wird. Ein Rückgabewert von null o void lässt ihn zur gleichen Seite zurückkehren und hält den aktuellen Ansichtsbereich am Leben. Ein Rückgabewert von einer leeren Zeichenkette oder der gleichen View-ID führt ebenfalls zur gleichen Seite zurück, erstellt aber den View-Bereich neu und zerstört somit alle derzeit aktiven View-Scoped Beans und erstellt sie gegebenenfalls neu.

En action kann jede gültige Methode sein MethodExpression auch diejenigen, 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 lediglich eine Zeichenkette zurückgibt, auch genau diese Zeichenkette in der action Attribut. Dies ist also völlig ungeschickt:

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

Mit dieser sinnlosen Methode, die eine hartkodierte Zeichenfolge zurückgibt:

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

Geben Sie stattdessen einfach die hartkodierte 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: die Navigation per POST. Dies ist weder benutzer- noch SEO-freundlich. Dies alles wird erklärt in Wann sollte ich h:outputLink anstelle von h:commandLink verwenden? und soll gelöst werden als

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

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


f:ajax Zuhörer

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

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

En ajaxListener Methode hat standardmäßig die folgende Signatur:

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

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

In Mojarra, dem AjaxBehaviorEvent Argument ist optional, unten funktioniert es genauso gut.

public void ajaxListener() {
    // ...
}

Aber in MyFaces, würde es eine MethodNotFoundException . Unten funktioniert in beiden JSF-Implementierungen, wenn Sie das Argument weglassen wollen.

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

Ajax-Listener sind bei Befehlskomponenten nicht wirklich nützlich. Sie sind nützlicher für Eingabe- und Auswahlkomponenten <h:inputXxx> / <h:selectXxx> . In Befehlskomponenten bleiben Sie einfach bei action und/oder actionListener für mehr Klarheit und eine bessere Selbstdokumentation des Codes. Außerdem, wie actionListener die f:ajax listener unterstützt nicht die Rückgabe eines Navigationsergebnisses.

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

Zur Erklärung von execute y render Attribute, gehen Sie zu Verständnis von PrimeFaces process/update und JSF f:ajax execute/render attributes .


Aufforderungsanordnung

En actionListener s werden immer aufgerufen antes de die action in der gleichen Reihenfolge, in der sie in der Ansicht deklariert und an die Komponente angehängt wurden. Die f:ajax listener wird immer aufgerufen antes de jede Aktion Hörer. 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()

Behandlung von Ausnahmen

En actionListener unterstützt eine besondere Ausnahme: AbortProcessingException . Wenn diese Ausnahme von einer actionListener Methode, dann überspringt JSF alle verbleibenden Action-Listener und die Action-Methode und fährt direkt mit dem Rendern der Antwort fort. Es wird keine Fehler-/Ausnahmeseite angezeigt, JSF protokolliert sie jedoch. Dies geschieht auch implizit immer dann, wenn eine andere Ausnahme von einer anderen Methode geworfen wird. actionListener . Wenn Sie also beabsichtigen, die Seite durch eine Fehlerseite als Ergebnis einer Geschäftsausnahme zu blockieren, dann sollten Sie auf jeden Fall die Arbeit in der action Methode.

Wenn der einzige Grund für die Verwendung eines actionListener ist es, eine void Methode, die zur gleichen Seite zurückkehrt, dann ist das schlecht. Die action Methoden können perfekt auch zurückgeben void im Gegensatz zu dem, was einige IDEs über die EL-Validierung glauben machen. Beachten Sie, dass die PrimeFaces Schaufenster Die Beispiele sind voll von dieser Art von actionListener s über den ganzen Platz. Das ist in der Tat falsch. Benutzen Sie das nicht als Ausrede, um das auch selbst zu tun.

Bei Ajax-Anfragen ist jedoch ein spezieller Exception-Handler erforderlich. Dies ist unabhängig davon, ob Sie listener Attribut von <f:ajax> oder nicht. Eine Erklärung und ein Beispiel finden Sie unter Ausnahmebehandlung in JSF-Ajax-Anfragen .

1 Stimmen

Du hast recht, dass Ausnahmen in actionListeners standardmäßig verschluckt werden, aber in JSF 2.0 kann dieses Verhalten geändert werden. Siehe unten meine Antwort für Details.

3 Stimmen

@arjan: Du hast recht, dass JSF 2.0 es ermöglicht, die standardmäßige Behandlung von Ausnahmen, die von actionListener geworfen werden, zu ändern, aber das ist immer noch keine gute Entschuldigung, actionListener für Geschäftsvorgänge zu missbrauchen.

1 Stimmen

Tatsächlich sind Geschäftsaktionen hauptsächlich im "Fluss" des Anforderungs-/Antwortzyklus und nur die action entspricht dem. actionListener ist für sekundäre Dinge. Wollte nur klarstellen, dass Ausnahmen von actionListeners weitergeleitet werden können, wenn dies erforderlich ist ;)

48voto

Arjan Tijms Punkte 37226

Wie BalusC angibt, verschluckt der actionListener standardmäßig Ausnahmen, aber in JSF 2.0 ist das ein wenig komplizierter. Genauer gesagt, es verschluckt die Ausnahme nicht einfach nur und protokolliert sie, sondern veröffentlicht sie tatsächlich.

Dies geschieht über einen Aufruf wie diesen:

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

Der Standard-Listener für dieses Ereignis ist der ExceptionHandler, der für Mojarra auf com.sun.faces.context.ExceptionHandlerImpl gesetzt ist. Diese Implementierung gibt im Wesentlichen jede Ausnahme weiter, außer wenn es sich um eine AbortProcessingException handelt, die protokolliert wird. ActionListeners verpacken die Ausnahme, die vom Clientcode ausgelöst wird, in eine solche AbortProcessingException, was erklärt, warum diese immer protokolliert werden.

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

   com.foo.myExceptionHandler

Anstelle global zuzuhören, kann auch eine einzige Bean auf diese Ereignisse hören. Hier 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");
    }

}

(Anmerkung: So sollte man normalerweise keine Listener codieren, dies dient nur zu Demonstrationszwecken!)

Wenn dies von einem Facelet wie folgt aufgerufen wird:

Wird eine Fehlerseite angezeigt.

44voto

Erick Robertson Punkte 30890

ActionListener wird zuerst ausgelöst, mit der Möglichkeit, die Antwort zu modifizieren, bevor Action aufgerufen wird und den Ort der nächsten Seite bestimmt.

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

Hier ist ein Link, der die Beziehung beschreibt:

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

4 Stimmen

Plus eins, Die fetten Buchstaben sagen fast alles.

0voto

Yehuda Schwartz Punkte 2879

Zusammenfassung:

Die ActionListener (es können mehrere sein) werden in der Reihenfolge ausgeführt, in der sie VOR dem action registriert wurden

Lange Antwort:

Ein geschäftliches action ruft typischerweise einen EJB-Service auf und setzt bei Bedarf auch das Endergebnis und/oder navigiert zu einer anderen Ansicht. Wenn dies nicht das ist, was Sie tun, ist ein actionListener eher angebracht, d.h. wenn der Benutzer mit den Komponenten interagiert, wie z.B. h:commandButton oder h:link, können sie behandelt werden, indem der Name der verwalteten Bean-Methode im actionListener-Attribut eines UI-Components übergeben wird oder indem eine ActionListener-Schnittstelle implementiert und der Klassenname der Implementierung an das actionListener-Attribut eines UI-Components übergeben wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X