В чем разница между 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 завалены этим видом actionListeners повсюду. Это действительно неправильно. Не используйте это как оправдание, чтобы сделать это самостоятельно.
Однако в запросах ajax требуется специальный обработчик исключений. Это независимо от того, используете ли вы listenerатрибут <f:ajax>или нет. Для объяснения и примера обратитесь к обработке исключений в запросах JSF ajax .
actionListener, но это еще не делает его хорошим оправданием злоупотреблений actionListenerдля деловых действий.
actionсоответствуют этому. actionListenerдля второстепенных вещей. Просто хотел уточнить, что исключения из actionListeners могут распространяться при необходимости;)
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)
);
По умолчанию для этого события используется прослушиватель ExceptionHandlerwhich для 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 :
В ActionListeners (может быть несколько) выполняются в порядке их регистрации доaction
Длинный ответ :
Бизнес actionобычно вызывает службу EJB и при необходимости также устанавливает конечный результат и / или переходит к другому представлению, если это не то, что вы делаете, actionListenerболее уместно, например, когда пользователь взаимодействует с компонентами, такими как h:commandButtonили h:linkони могут обрабатываться путем передачи имени метода управляемого компонента в actionListenerатрибуте компонента UI или для реализации ActionListenerинтерфейса и передачи имени класса реализации в actionListenerатрибут компонента UI.