Различия между действием и actionListener - PullRequest
372 голосов
/ 11 октября 2010

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

Ответы [ 4 ]

560 голосов
/ 11 октября 2010

actionListener

Используйте actionListener, если хотите получить хук перед выполнением реального бизнес-действия, например, для его регистрации и / или для установки дополнительного свойства (by <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>.

* 1033.*
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+ прозрачно превращает выражение значения в выражение метода.См. Также Почему я могу связыватьсяк произвольному методу, если он не поддерживается JSF?


action

Используйте 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 отражал текущую страницу (а не предыдущую) .


f: слушатель 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 всегда вызываются до 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> или нет. Для объяснения и примера обратитесь к Обработка исключений в запросах JSF ajax .

45 голосов
/ 19 декабря 2010

Как указал 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>

В результате отобразится страница с ошибкой.

39 голосов
/ 11 октября 2010

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

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

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

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

0 голосов
/ 28 ноября 2018

TL; DR

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

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

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

...