В чем разница между action
и actionListener
, и когда я должен использовать action
против actionListener
?
В чем разница между action
и actionListener
, и когда я должен использовать action
против 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>
.
<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. Почему я могу связать <f: actionListener> с произвольным методом, если он не поддерживается 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 отражал текущую страницу (а не предыдущую) .
Начиная с 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 execute / render .
В 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>
Вызовет методы в следующем порядке:
Bean#ajaxListener()
Bean#actionListener()
ActionListenerType#processAction()
Bean#actionListenerBinding()
Bean#setProperty()
Bean#action()
actionListener
Поддерживает специальное исключение: AbortProcessingException
. Если это исключение выдается из actionListener
метода, то JSF пропустит все оставшиеся прослушиватели действий и метод действия и продолжит обработку ответа напрямую. Вы не увидите страницу ошибки / исключения, однако JSF зарегистрирует ее. Это также неявно будет выполняться всякий раз, когда из исключения выдается любое другое исключение actionListener
. Таким образом, если вы намереваетесь заблокировать страницу страницей с ошибкой в результате бизнес-исключения, то вы обязательно должны выполнить работу в action
методе.
Если единственная причина , чтобы использовать actionListener
это , чтобы иметь void
метод , возвращающий к одной и той же странице, то это плохой. Эти action
методы вполне могут также возвращать void
, наоборот тому , что некоторые Иды пусть вы считаете , с помощью проверки EL. Обратите внимание, что примеры витрин PrimeFaces завалены этим видом actionListener
s повсюду. Это действительно неправильно. Не используйте это как оправдание, чтобы сделать это самостоятельно.
Однако в запросах ajax требуется специальный обработчик исключений. Это независимо от того, используете ли вы listener
атрибут <f:ajax>
или нет. Для объяснения и примера обратитесь к обработке исключений в запросах JSF ajax .
actionListener
, но это еще не делает его хорошим оправданием злоупотреблений actionListener
для деловых действий.
action
соответствуют этому. actionListener
для второстепенных вещей. Просто хотел уточнить, что исключения из actionListener
s могут распространяться при необходимости;)
actionListener
атрибуте, и оно должно быть public
также. processAction
Имя только обязательным , если вы используете <f:actionListener type>
, просто потому , что тип должен реализовывать ActionListener
интерфейс , который имеет именно это имя метода processAction
определяемом.
<f:ajax>
вы в случае командных компонентов предпочитаете использовать action
атрибут для бизнес-действий. Например <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>
.
Как указал BalusC, actionListener
по умолчанию исключаются исключения, но в JSF 2.0 есть еще кое-что . А именно, он не просто глотает и регистрирует, но фактически публикует исключение.
Это происходит через вызов, как это:
context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,
new ExceptionQueuedEventContext(context, exception, source, phaseId)
);
По умолчанию для этого события используется прослушиватель ExceptionHandler
which для Mojarra 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
атрибуте компонента UI или для реализации ActionListener
интерфейса и передачи имени класса реализации в actionListener
атрибут компонента UI.