Was ist der Unterschied zwischen action
und actionListener
, und wann soll ich im action
Vergleich zu actionListener
?
Was ist der Unterschied zwischen action
und actionListener
, und wann soll ich im action
Vergleich zu actionListener
?
Antworten:
Verwenden actionListener
Sie diese Option, wenn Sie einen Hook haben möchten, bevor die eigentliche Geschäftsaktion ausgeführt wird, z. B. um sie zu protokollieren und / oder um eine zusätzliche Eigenschaft (von <f:setPropertyActionListener>
) festzulegen und / oder um Zugriff auf die Komponente zu haben, die die Aktion aufgerufen hat (die von verfügbar ist) ActionEvent
Streit). Also nur zu Vorbereitungszwecken, bevor die eigentliche Geschäftsaktion aufgerufen wird.
Die 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 Methodenklammern:
<h:commandXxx ... actionListener="#{bean.actionListener}" />
Beachten Sie, dass Sie von EL 2.2 keine zusätzlichen Argumente übergeben können. Sie können das ActionEvent
Argument jedoch vollständig überschreiben, indem Sie benutzerdefinierte Argumente übergeben und angeben. 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 im argumentlosen Methodenausdruck. Wenn sie nicht vorhanden wären, würde JSF immer noch eine Methode mit ActionEvent
Argumenten erwarten .
Wenn Sie mit EL 2.2+ arbeiten, können Sie über mehrere Aktionslistener-Methoden deklarieren <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 Wichtigkeit der Klammern im binding
Attribut. Wenn sie nicht vorhanden wären, würde EL verwirrenderweise a auslösen javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean
, da das binding
Attribut standardmäßig als Werteausdruck 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 ao Warum kann ich <f: actionListener> an eine beliebige Methode binden, wenn diese von JSF nicht unterstützt wird?
Verwenden action
Sie diese Option, wenn Sie eine Geschäftsaktion ausführen und gegebenenfalls die Navigation übernehmen möchten. Die action
Methode kann (muss also nicht) ein zurückgeben String
, das als Navigationsfallergebnis (die Zielansicht) verwendet wird. Ein Rückgabewert von null
oder void
lässt ihn zur gleichen Seite zurückkehren und den aktuellen Ansichtsbereich am Leben erhalten. Ein Rückgabewert einer leeren Zeichenfolge oder derselben Ansichts-ID kehrt ebenfalls zur gleichen Seite zurück, erstellt jedoch den Ansichtsbereich neu und zerstört somit alle derzeit aktiven Beans mit Ansichtsbereich und erstellt sie gegebenenfalls neu.
Die action
Methode kann eine beliebige sein MethodExpression
, 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 action
Attribut 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 einfach diese 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: Navigieren per POST. Dies ist weder benutzer- noch SEO-freundlich. Dies alles wird unter Wann soll ich h: outputLink anstelle von h: commandLink verwenden? und soll gelöst werden als
<h:link value="Go to next page" outcome="nextpage" />
Seit JSF 2.x gibt es einen dritten Weg, den <f:ajax listener>
.
<h:commandXxx ...>
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
Die ajaxListener
Methode hat standardmäßig die folgende Signatur:
import javax.faces.event.AjaxBehaviorEvent;
// ...
public void ajaxListener(AjaxBehaviorEvent event) {
// ...
}
In Mojarra ist das AjaxBehaviorEvent
Argument optional, unten funktioniert es genauso gut.
public void ajaxListener() {
// ...
}
Aber in MyFaces würde es ein werfen MethodNotFoundException
. 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 nützlicher bei der Eingabe und Auswahl von Komponenten <h:inputXxx>
/ <h:selectXxx>
. Halten Sie sich in Befehlskomponenten einfach an action
und / oder actionListener
aus Gründen der Klarheit und besseren Selbstdokumentation des Codes. Darüber hinaus unterstützt actionListener
das f:ajax listener
nicht die Rückgabe eines Navigationsergebnisses.
<h:commandXxx ... action="#{bean.action}">
<f:ajax execute="@form" render="@form" />
</h:commandXxx>
Erläuterungen zu execute
und render
Attributen finden Sie unter Grundlegendes zum PrimeFaces-Prozess / Update und zu JSF f: ajax execute / render-Attributen .
Die actionListener
s werden immer vor dem action
in derselben Reihenfolge aufgerufen, in der sie in der Ansicht deklariert und an die Komponente angehängt wurden. Das f:ajax listener
wird immer vor einem Aktionslistener aufgerufen . 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()
Das actionListener
unterstützt eine besondere Ausnahme : AbortProcessingException
. Wenn diese Ausnahme von einer actionListener
Methode ausgelöst wird, überspringt JSF alle verbleibenden Aktionslistener und die Aktionsmethode und fährt mit dem direkten Rendern der Antwort fort. Sie sehen keine Fehler- / Ausnahmeseite, JSF protokolliert sie jedoch. Dies wird implizit auch dann durchgeführt, wenn eine andere Ausnahme von einem ausgelöst wird actionListener
. Wenn Sie also beabsichtigen, die Seite aufgrund einer Geschäftsausnahme durch eine Fehlerseite zu blockieren, sollten Sie den Job auf jeden Fall in der action
Methode ausführen .
Wenn der einzige Grund für die Verwendung actionListener
von eine void
Methode ist, die zur gleichen Seite zurückkehrt, ist dies eine schlechte Methode. Die action
Methoden können auch perfekt zurückkehren void
, im Gegensatz zu dem, was einige IDEs Ihnen über die EL-Validierung glauben machen. Beachten Sie, dass die PrimeFaces-SchaufensterbeispieleactionListener
überall mit solchen s übersät sind . Das ist in der Tat falsch. Verwenden Sie dies nicht als Ausrede, um dies auch selbst zu tun.
Bei Ajax-Anforderungen wird jedoch ein spezieller Ausnahmebehandler benötigt. Dies ist unabhängig davon, ob Sie das listener
Attribut von verwenden <f:ajax>
oder nicht. Eine Erläuterung und ein Beispiel finden Sie unter Ausnahmebehandlung in JSF-Ajax-Anforderungen .
actionListener
jedoch keine gute Entschuldigung für den Missbrauch actionListener
von Geschäftsaktionen .
action
entspricht dem. actionListener
ist für sekundäre Sachen. Ich wollte nur klarstellen, dass Ausnahmen von actionListener
s verbreitet werden können, wenn dies erforderlich ist;)
actionListener
Attribut frei gewählt werden und muss es public
auch sein. Der processAction
Name ist nur obligatorisch, wenn Sie ihn verwenden <f:actionListener type>
, einfach weil der Typ eine ActionListener
Schnittstelle implementieren muss , für die genau dieser Methodenname processAction
definiert ist.
<f:ajax>
von Befehlskomponenten das action
Attribut für Geschäftsaktionen bevorzugen . ZB <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>
.
Wie BalusC angedeutet hat, actionListener
werden standardmäßig Ausnahmen verschluckt, aber in JSF 2.0 steckt noch ein bisschen mehr dahinter . Das heißt, es nicht nur Schwalben und Protokolle, aber tatsächlich veröffentlicht die Ausnahme.
Dies geschieht durch einen Anruf wie folgt:
context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,
new ExceptionQueuedEventContext(context, exception, source, phaseId)
);
Der Standard-Listener für dieses Ereignis ist der ExceptionHandler
für Mojarra festgelegte com.sun.faces.context.ExceptionHandlerImpl
. Diese Implementierung löst grundsätzlich alle Ausnahmen erneut aus, es sei denn, es handelt sich um eine AbortProcessingException, die protokolliert wird. ActionListener schließen die vom Clientcode ausgelöste Ausnahme in eine solche AbortProcessingException ein, in der erklärt wird, warum diese immer protokolliert werden.
Dies ExceptionHandler
kann jedoch in der Datei features-config.xml durch eine benutzerdefinierte Implementierung ersetzt werden:
<exception-handlerfactory>
com.foo.myExceptionHandler
</exception-handlerfactory>
Anstatt global zuzuhören, 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");
}
}
(Beachten Sie, dass man Hörer normalerweise nicht so codieren sollte, dies dient nur zu Demonstrationszwecken!)
Nennen Sie dies 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>
Es wird eine Fehlerseite angezeigt.
ActionListener wird zuerst ausgelöst, mit einer Option zum Ändern der Antwort, bevor Action aufgerufen wird und den Speicherort der nächsten Seite bestimmt.
Wenn Sie mehrere Schaltflächen auf derselben Seite haben, die an dieselbe Stelle gehen sollen, aber leicht unterschiedliche Aufgaben ausführen, können Sie für jede Schaltfläche dieselbe Aktion verwenden, jedoch einen anderen ActionListener verwenden, um geringfügig unterschiedliche Funktionen zu verarbeiten.
Hier ist ein Link, der die Beziehung beschreibt:
TL; DR :
Die ActionListener
s (es können mehrere sein) werden in der Reihenfolge ausgeführt, in der sie VOR dem registriert wurdenaction
Lange Antwort :
Ein Unternehmen action
ruft normalerweise einen EJB-Service auf und legt bei Bedarf auch das Endergebnis fest und / oder navigiert zu einer anderen Ansicht, wenn dies nicht das ist, was Sie tun, und actionListener
ist besser geeignet, dh wenn der Benutzer mit den Komponenten interagiert, wie h:commandButton
oder h:link
können sie Sie müssen den Namen der verwalteten Bean-Methode im actionListener
Attribut einer UI-Komponente übergeben oder eine ActionListener
Schnittstelle implementieren und den Namen der Implementierungsklasse an das actionListener
Attribut einer UI-Komponente übergeben.