Was ist der Unterschied zwischen action
und actionListener
, und wann sollte ich action
im Vergleich zu actionListener
verwenden?
Verwenden Sie actionListener
name__, 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) ActionEvent
name __ argument). Rein zu Vorbereitungszwecken, bevor die eigentliche Geschäftsaktion aufgerufen wird.
Die Methode actionListener
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 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 ActionEvent
insgesamt ü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 ActionEvent
erwarten.
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 binding
name__. 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 binding
standardmäß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?
Verwenden Sie action
name__, wenn Sie eine Geschäftsaktion ausführen und gegebenenfalls die Navigation verwalten möchten. Die Methode action
kann (muss also nicht) einen String
namen__ zurückgeben, der als Navigationsfallergebnis (die Zielansicht) verwendet wird. Mit dem Rückgabewert null
oder void
wird 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 action
kann ein beliebiges gültiges MethodExpression
NAME _ 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 action
angeben 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 .
Seit JSF 2.x gibt es einen dritten Weg, den _<f:ajax listener>
_.
_<h:commandXxx ...>
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
_
Die Methode ajaxListener
hat standardmäßig die folgende Signatur:
_import javax.faces.event.AjaxBehaviorEvent;
// ...
public void ajaxListener(AjaxBehaviorEvent event) {
// ...
}
_
In Mojarra ist das Argument AjaxBehaviorEvent
optional (siehe unten).
_public void ajaxListener() {
// ...
}
_
In MyFaces würde es jedoch einen MethodNotFoundException
auslö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 action
und/oder actionListener
name__, um die Übersichtlichkeit und den selbstdokumentierenden Code zu verbessern. Wie actionListener
unterstü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 execute
und render
finden Sie unter Grundlegendes zu PrimeFaces-Prozessen/-Updates und JSF f: Ajax Execute/Render-Attributen .
Die actionListener
name__s werden immer aufgerufen vor der action
in 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:
Bean#ajaxListener()
Bean#actionListener()
ActionListenerType#processAction()
Bean#actionListenerBinding()
Bean#setProperty()
Bean#action()
Der actionListener
unterstützt eine spezielle Ausnahme: AbortProcessingException
NAME _ . Wenn diese Ausnahme von einer actionListener
name__-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 actionListener
geworfen 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 action
ausführen.
Wenn der einzige Grund für die Verwendung einer actionListener
darin besteht, dass eine void
name__-Methode zur selben Seite zurückkehrt, ist dies eine schlechte. Die action
name__-Methoden können auch void
zurü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 actionListener
name__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 listener
von _<f:ajax>
_ verwenden oder nicht. Zur Erläuterung und als Beispiel gehen Sie zu Ausnahmebehandlung in JSF-Ajax-Anforderungen .
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.
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:
TL; DR :
Die ActionListener
s (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.