Различия между действием и actionListener

В чем разница между action а также actionListenerи когда я должен использовать action против actionListener?

4 ответа

Решение

ActionListener

использование actionListener если вы хотите получить хук до того, как будет выполнено реальное бизнес-действие, например, чтобы зарегистрировать его и / или установить дополнительное свойство (путем <f:setPropertyActionListener>) и / или иметь доступ к компоненту, вызвавшему действие (которое доступно ActionEvent аргумент). Таким образом, чисто для подготовки целей, прежде чем будет начато реальное деловое действие.

actionListener По умолчанию метод имеет следующую подпись:

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

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

И это должно быть объявлено следующим образом, без каких-либо скобок метода:

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

Обратите внимание, что вы не можете передать дополнительные аргументы по EL 2.2. Однако вы можете переопределить 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 приписывать. Если бы они отсутствовали, EL смущенно бросил бы javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean, поскольку binding Атрибут по умолчанию интерпретируется как выражение значения, а не как выражение метода. Добавление скобок в стиле EL 2.2+ прозрачно превращает выражение значения в выражение метода. См. Также ao. Почему я могу связать с произвольным методом, если он не поддерживается JSF?


действие

использование action если вы хотите выполнить бизнес-действие и при необходимости обработать навигацию. action метод может (таким образом, не должен) возвращать String который будет использоваться в качестве результата навигации (целевой вид). Возвращаемое значение null или же void позволит ему вернуться на ту же страницу и сохранить текущую область просмотра. Возвращаемое значение пустой строки или того же идентификатора представления также вернется на ту же страницу, но заново создаст область видимости и, таким образом, уничтожит все активные в настоящее время компоненты области видимости и, если применимо, создаст их заново.

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" />

Обратите внимание, что это, в свою очередь, указывает на плохой дизайн: навигация по POST. Это не для пользователя и не оптимизировано для SEO. Все это объясняется в разделе Когда я должен использовать h:outputLink вместо h:commandLink? и должен быть решен как

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

Смотрите также Как ориентироваться в JSF? Как сделать так, чтобы URL отражал текущую страницу (а не предыдущую).


слушатель ajax

Начиная с JSF 2.x существует третий путь, <f:ajax listener>,

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

ajaxListener По умолчанию метод имеет следующую подпись:

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

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

В Мохарре 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 и JSF f:ajax для выполнения / рендеринга атрибутов.


Порядок вызова

actionListener s всегда вызываются до action в том же порядке, в каком они были объявлены в представлении и прикреплены к компоненту. f:ajax listener всегда вызывается перед любым слушателем действия. Итак, следующий пример:

<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 завалены этим видом actionListener S повсюду. Это действительно неправильно. Не используйте это как оправдание, чтобы сделать это самостоятельно.

Однако в запросах ajax требуется специальный обработчик исключений. Это независимо от того, используете ли вы listener атрибут <f:ajax> или нет. Для объяснения и примера обратитесь к обработке исключений в запросах ajax JSF.

Как указал BalusC, actionListener по умолчанию проглатывает исключения, но в JSF 2.0 есть еще кое-что. А именно, он не просто глотает и регистрирует, но фактически публикует исключение.

Это происходит через вызов, как это:

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

Слушателем по умолчанию для этого события является ExceptionHandler который для Мохарры установлен в com.sun.faces.context.ExceptionHandlerImpl, Эта реализация будет в основном перебрасывать любое исключение, за исключением случаев, когда оно касается исключения AbortProcessingException, которое регистрируется. ActionListeners оборачивают исключение, которое выдается клиентским кодом, в такое исключение AbortProcessingException, которое объясняет, почему они всегда регистрируются.

это ExceptionHandler может быть заменен, однако, в face-config.xml с пользовательской реализацией:

<exception-handlerfactory>
   com.foo.myExceptionHandler
</exception-handlerfactory>

Вместо глобального прослушивания, один компонент также может прослушивать эти события. Следующее является доказательством концепции этого:

@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>

Это приведет к отображению страницы ошибки.

Сначала запускается ActionListener с возможностью изменения ответа до вызова Action и определения местоположения следующей страницы.

Если у вас есть несколько кнопок на одной странице, которые должны идти в одно и то же место, но выполнять немного разные вещи, вы можете использовать одно и то же действие для каждой кнопки, но использовать другой ActionListener для обработки немного другой функциональности.

Вот ссылка, которая описывает отношения:

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

TL; DR:

ActionListeners (их может быть несколько) выполняются в том порядке, в котором они были зарегистрированы ДО action

Длинный ответ:

Дело action обычно вызывает службу EJB и, при необходимости, также устанавливает конечный результат и / или переходит к другому представлению, если это не то, что вы делаете actionListener является более подходящим, например, когда пользователь взаимодействует с компонентами, такими как h:commandButton или же h:link они могут быть обработаны путем передачи имени метода управляемого компонента в actionListener атрибут компонента пользовательского интерфейса или для реализации ActionListener интерфейс и передать имя класса реализации actionListener атрибут компонента пользовательского интерфейса.

Другие вопросы по тегам