web-dev-qa-db-ja.com

actionとactionListenerの違い

actionactionListenerの違いは何ですか?またactionactionListenerはいつ使用すべきですか?

369
Murat Güzel

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に現在のページを反映させる方法(前のページではない) も参照。


f:ajaxリスナー

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>

次の順序でメソッドを呼び出します。

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

例外処理

actionListenerは、特殊な例外、 AbortProcessingException をサポートしています。この例外がactionListenerメソッドからスローされた場合、JSFは残りのアクションリスナーとアクションメソッドをスキップして、直接応答をレンダリングします。エラー/例外ページは表示されませんが、JSFはそれをログに記録します。これはactionListenerから他の例外が投げられているときにも暗黙のうちに行われます。そのため、ビジネス例外の結果としてエラーページでページをブロックする予定がある場合は、必ずactionメソッドでジョブを実行してください。

actionListenerを使用する唯一の理由が同じページに戻るvoidメソッドを使用することである場合、それは悪い方法です。 actionメソッドは完全にvoidを返すこともできます。これは、一部のIDEでEL検証を介して実行されているとは対照的です。 PrimeFaces showcase の例は、あらゆるところにこのようなactionListenerが散らばっていることに注意してください。これは間違いです。これを自分自身で行うための言い訳として使用しないでください。

ただし、ajaxリクエストでは、特別な例外ハンドラが必要です。これは<f:ajax>listener属性を使うかどうかには関係ありません。説明と例については、 JSF ajax要求での例外処理 に進んでください。

558
BalusC

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>

エラーページが表示されます。

45
Arjan Tijms

Actionが呼び出されて次のページの位置を決定する前に、ActionListenerが最初に起動され、応答を変更するオプションがあります。

同じページに複数のボタンがあり、それらが同じ場所に移動する必要がある場合は、各ボタンに同じActionを使用できますが、異なるActionListenerを使用してわずかに異なる機能を処理できます。

これが関係を説明するリンクです。

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

39
Erick Robertson

TL; DR

ActionListeners(複数ある場合もあります)は、actionの前に登録された順序で実行されます。

ロングアンサー

ビジネスactionは通常EJBサービスを呼び出し、必要に応じて最終結果を設定したり、actionListenerが適切でない場合は別のビューに移動します。 h:commandButtonh:linkなどのコンポーネントとやり取りするには、UIコンポーネントのactionListener属性にマネージドBeanメソッドの名前を渡すか、ActionListenerインターフェースを実装して実装クラス名を渡すことで処理できます。 UIコンポーネントのactionListener属性へ。

0
Yehuda Schwartz