action
とactionListener
の違いは何ですか?またaction
とactionListener
はいつ使用すべきですか?
フックを使用したい場合はactionListener
を使用します 以前 実際のビジネスアクションが実行されます。それをログに記録したり、追加のプロパティを( <f:setPropertyActionListener>
で)設定したり、アクションを呼び出したコンポーネントにアクセスしたりする( ActionEvent
引数で利用可能) 。つまり、純粋なビジネスアクションが実行される前に純粋に目的を準備するためのものです。
actionListener
メソッドには、デフォルトで次のシグネチャがあります。
import javax.faces.event.ActionEvent;
// ...
public void actionListener(ActionEvent event) {
// ...
}
そして、メソッドの括弧なしで、次のように宣言されることになっています:
<h:commandXxx ... actionListener="#{bean.actionListener}" />
EL 2.2ではadditionalarguments)を渡すことはできません。ただし、カスタム引数を渡して指定することで、ActionEvent
引数をすべてオーバーライドすることはできます。次の例は有効です。
<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) {}
引数のないメソッド式における括弧の重要性に注意してください。それらが欠けていても、JSFはActionEvent
引数を持つメソッドを期待します。
もしあなたがEL 2.2+を使っているのなら、<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() {}
binding
属性の括弧の重要性に注意してください。 binding
属性はデフォルトでメソッド式ではなく値式として解釈されるため、ELが混乱してjavax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean
をスローします。 EL 2.2+スタイルの括弧を追加すると、値式がメソッド式に透過的に変わります。 a.o.も参照してください。 JSFでサポートされていない場合、<f:actionListener>を任意のメソッドにバインドできるのはなぜですか?
ビジネスアクションを実行し、必要に応じてナビゲーションを処理する場合はaction
を使用します。 action
メソッドは、ナビゲーションケースの結果(ターゲットビュー)として使用されるString
を返すことができます(したがって、必須ではありません)。戻り値null
またはvoid
を指定すると、同じページに戻り、現在のビュースコープが維持されます。空の文字列または同じビューIDの戻り値も同じページに戻りますが、ビュースコープを再作成して、現在アクティブなビュースコープのBeanをすべて破棄し、必要に応じてそれらを再作成します。
action
メソッドは任意の有効な MethodExpression
にすることができます。以下のようにEL 2.2引数を使用するものも同様です。
<h:commandXxx value="submit" action="#{bean.edit(item)}" />
この方法では:
public void edit(Item item) {
// ...
}
アクションメソッドが単独で文字列を返す場合は、その文字列をaction
属性に指定することもできます。したがって、これはまったく不器用です。
<h:commandLink value="Go to next page" action="#{bean.goToNextpage}" />
無意味な方法でハードコードされた文字列を返す:
public String goToNextpage() {
return "nextpage";
}
代わりに、そのハードコードされた文字列を直接属性に入れてください。
<h:commandLink value="Go to next page" action="nextpage" />
これは今度は悪いデザインを示していることに注意してください。これはユーザーにとってもSEOにも優しくありません。これはすべて{ h:commandLinkの代わりにh:outputLinkを使うべきなのか? で説明されており、次のように解決されるはずです。
<h:link value="Go to next page" outcome="nextpage" />
JSF内をナビゲートする方法?URLに現在のページを反映させる方法(前のページではない) も参照。
JSF 2.x以降、3つ目の方法、<f:ajax listener>
があります。
<h:commandXxx ...>
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
ajaxListener
メソッドには、デフォルトで次のシグネチャがあります。
import javax.faces.event.AjaxBehaviorEvent;
// ...
public void ajaxListener(AjaxBehaviorEvent event) {
// ...
}
Mojarraでは、AjaxBehaviorEvent
引数はオプションですが、以下のように動作します。
public void ajaxListener() {
// ...
}
しかしMyFacesでは、MethodNotFoundException
がスローされます。引数を省略したい場合、以下は両方のJSF実装で機能します。
<h:commandXxx ...>
<f:ajax execute="@form" listener="#{bean.ajaxListener()}" render="@form" />
</h:commandXxx>
Ajaxリスナーはコマンドコンポーネントにはあまり役に立ちません。これらは入力コンポーネントと選択コンポーネント<h:inputXxx>
/<h:selectXxx>
でより便利です。コマンドコンポーネントでは、明快さと自己文書化コードを向上させるために、action
またはactionListener
、あるいはその両方を使用してください。さらに、actionListener
のように、f:ajax listener
はナビゲーション結果を返すことをサポートしません。
<h:commandXxx ... action="#{bean.action}">
<f:ajax execute="@form" render="@form" />
</h:commandXxx>
execute
およびrender
属性の説明については、 PrimeFaces process/updateおよびJSF f:ajax実行/レンダリング属性について に進んでください。
actionListener
は常にbefore/ action
がビューで宣言され、コンポーネントにアタッチされたのと同じ順序で呼び出されます。f:ajax listener
は常にbeforeanyアクションリスナーの前に呼び出されます。
<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>
次の順序でメソッドを呼び出します。
Bean#ajaxListener()
Bean#actionListener()
ActionListenerType#processAction()
Bean#actionListenerBinding()
Bean#setProperty()
Bean#action()
actionListener
は、特殊な例外、 AbortProcessingException
をサポートしています。この例外がactionListener
メソッドからスローされた場合、JSFは残りのアクションリスナーとアクションメソッドをスキップして、直接応答をレンダリングします。エラー/例外ページは表示されませんが、JSFはそれをログに記録します。これはactionListener
から他の例外が投げられているときにも暗黙のうちに行われます。そのため、ビジネス例外の結果としてエラーページでページをブロックする予定がある場合は、必ずaction
メソッドでジョブを実行してください。
actionListener
を使用する唯一の理由が同じページに戻るvoid
メソッドを使用することである場合、それは悪い方法です。 action
メソッドは完全にvoid
を返すこともできます。これは、一部のIDEでEL検証を介して実行されているとは対照的です。 PrimeFaces showcase の例は、あらゆるところにこのようなactionListener
が散らばっていることに注意してください。これは間違いです。これを自分自身で行うための言い訳として使用しないでください。
ただし、ajaxリクエストでは、特別な例外ハンドラが必要です。これは<f:ajax>
のlistener
属性を使うかどうかには関係ありません。説明と例については、 JSF ajax要求での例外処理 に進んでください。
BalusCが示したように、デフォルトでのactionListener
は例外を飲み込みますが、JSF 2.0にはもう少しこれがあります。つまり、飲み込んでログに記録するだけではなく、実際には例外を公開します。
これは次のような呼び出しを通して起こります:
context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,
new ExceptionQueuedEventContext(context, exception, source, phaseId)
);
このイベントのデフォルトのリスナーはExceptionHandler
で、Mojarraではcom.Sun.faces.context.ExceptionHandlerImpl
に設定されています。この実装は、ログに記録されたAbortProcessingExceptionに関する場合を除き、基本的に例外をすべてスローします。 ActionListenerは、このようなAbortProcessingExceptionでクライアントコードによってスローされる例外をラップします。これにより、これらが常に記録される理由が説明されます。
しかしながら、このExceptionHandler
はfaces-config.xmlの中でカスタム実装に置き換えることができます。
<exception-handlerfactory>
com.foo.myExceptionHandler
</exception-handlerfactory>
グローバルにリッスンする代わりに、単一のBeanがこれらのイベントをリッスンすることもできます。以下はこの概念の証明です。
@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");
}
}
(注:これは通常リスナーをコーディングする方法ではありません。これはデモンストレーションを目的としたものです!)
このようにFaceletからこれを呼び出す:
<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>
エラーページが表示されます。
Actionが呼び出されて次のページの位置を決定する前に、ActionListenerが最初に起動され、応答を変更するオプションがあります。
同じページに複数のボタンがあり、それらが同じ場所に移動する必要がある場合は、各ボタンに同じActionを使用できますが、異なるActionListenerを使用してわずかに異なる機能を処理できます。
これが関係を説明するリンクです。
TL; DR:
ActionListener
s(複数ある場合もあります)は、action
の前に登録された順序で実行されます。
ロングアンサー:
ビジネスaction
は通常EJBサービスを呼び出し、必要に応じて最終結果を設定したり、actionListener
が適切でない場合は別のビューに移動します。 h:commandButton
やh:link
などのコンポーネントとやり取りするには、UIコンポーネントのactionListener
属性にマネージドBeanメソッドの名前を渡すか、ActionListener
インターフェースを実装して実装クラス名を渡すことで処理できます。 UIコンポーネントのactionListener
属性へ。