Различия между действием и 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. Почему я могу связать
действие
использование 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>
Вызовет методы в следующем порядке:
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 завалены этим видом 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 для обработки немного другой функциональности.
Вот ссылка, которая описывает отношения:
TL; DR:
ActionListener
s (их может быть несколько) выполняются в том порядке, в котором они были зарегистрированы ДО action
Длинный ответ:
Дело action
обычно вызывает службу EJB и, при необходимости, также устанавливает конечный результат и / или переходит к другому представлению, если это не то, что вы делаете actionListener
является более подходящим, например, когда пользователь взаимодействует с компонентами, такими как h:commandButton
или же h:link
они могут быть обработаны путем передачи имени метода управляемого компонента в actionListener
атрибут компонента пользовательского интерфейса или для реализации ActionListener
интерфейс и передать имя класса реализации actionListener
атрибут компонента пользовательского интерфейса.