Sự khác nhau giữa là gì action
và actionListener
, và khi tôi nên sử dụng action
so với actionListener
?
Sự khác nhau giữa là gì action
và actionListener
, và khi tôi nên sử dụng action
so với actionListener
?
Câu trả lời:
Sử dụng actionListener
nếu bạn muốn có một hook trước khi hành động kinh doanh thực sự được thực thi, ví dụ để ghi nhật ký và / hoặc để đặt một thuộc tính bổ sung (bởi <f:setPropertyActionListener>
) và / hoặc có quyền truy cập vào thành phần đã gọi hành động (có sẵn bởi ActionEvent
tranh luận). Vì vậy, hoàn toàn cho việc chuẩn bị các mục đích trước khi hành động kinh doanh thực sự được viện dẫn.
Các actionListener
phương pháp có theo mặc định chữ ký sau đây:
import javax.faces.event.ActionEvent;
// ...
public void actionListener(ActionEvent event) {
// ...
}
Và nó được cho là được khai báo như sau, không có dấu ngoặc đơn phương thức:
<h:commandXxx ... actionListener="#{bean.actionListener}" />
Lưu ý rằng bạn không thể vượt qua các đối số bổ sung của EL 2.2. Tuy nhiên, bạn có thể ghi đè ActionEvent
đối số hoàn toàn bằng cách chuyển và chỉ định (các) đối số tùy chỉnh. Các ví dụ sau là hợp lệ:
<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) {}
Lưu ý tầm quan trọng của dấu ngoặc trong biểu thức phương thức không đối số. Nếu họ vắng mặt, JSF vẫn mong đợi một phương thức có ActionEvent
đối số.
Nếu bạn đang sử dụng EL 2.2+, thì bạn có thể khai báo nhiều phương thức nghe hành động thông qua <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() {}
Lưu ý tầm quan trọng của dấu ngoặc trong binding
thuộc tính. Nếu chúng vắng mặt, EL sẽ nhầm lẫn ném một javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean
, vì binding
mặc định thuộc tính được hiểu là biểu thức giá trị, không phải là biểu thức phương thức. Thêm dấu ngoặc kiểu EL 2.2+ trong suốt biến biểu thức giá trị thành biểu thức phương thức. Xem thêm ao Tại sao tôi có thể liên kết <f: actionListener> với một phương thức tùy ý nếu nó không được hỗ trợ bởi JSF?
Sử dụng action
nếu bạn muốn thực hiện một hành động kinh doanh và nếu cần điều khiển điều hướng. Các action
phương pháp có thể (do đó, không phải) trả về một String
mà sẽ được sử dụng như trường hợp chuyển hướng kết quả (quan điểm mục tiêu). Giá trị trả về của null
hoặc void
sẽ cho phép nó trở về cùng một trang và giữ cho phạm vi chế độ xem hiện tại tồn tại. Giá trị trả về của một chuỗi trống hoặc cùng một ID chế độ xem cũng sẽ trở về cùng một trang, nhưng tạo lại phạm vi chế độ xem và do đó phá hủy mọi hạt đậu có phạm vi xem hiện đang hoạt động và, nếu có thể, sẽ tạo lại chúng.
Các action
phương pháp có thể được bất kỳ hợp lệ MethodExpression
, cũng là những người trong đó sử dụng EL 2.2 đối số như dưới đây:
<h:commandXxx value="submit" action="#{bean.edit(item)}" />
Với phương pháp này:
public void edit(Item item) {
// ...
}
Lưu ý rằng khi phương thức hành động của bạn chỉ trả về một chuỗi, thì bạn cũng có thể chỉ định chính xác chuỗi đó trong action
thuộc tính. Vì vậy, điều này là hoàn toàn vụng về:
<h:commandLink value="Go to next page" action="#{bean.goToNextpage}" />
Với phương thức vô nghĩa này, trả về một chuỗi mã hóa cứng:
public String goToNextpage() {
return "nextpage";
}
Thay vào đó, chỉ cần đặt chuỗi mã hóa cứng đó trực tiếp vào thuộc tính:
<h:commandLink value="Go to next page" action="nextpage" />
Xin lưu ý rằng điều này lần lượt chỉ ra một thiết kế xấu: điều hướng bằng POST. Đây không phải là người dùng cũng không thân thiện với SEO. Tất cả điều này được giải thích trong Khi nào tôi nên sử dụng h: outputLink thay vì h: commandLink? và được cho là sẽ được giải quyết
<h:link value="Go to next page" outcome="nextpage" />
Kể từ khi JSF 2.x có một cách thứ ba, <f:ajax listener>
.
<h:commandXxx ...>
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
Các ajaxListener
phương pháp có theo mặc định chữ ký sau đây:
import javax.faces.event.AjaxBehaviorEvent;
// ...
public void ajaxListener(AjaxBehaviorEvent event) {
// ...
}
Trong Mojarra, AjaxBehaviorEvent
đối số là tùy chọn, bên dưới hoạt động tốt.
public void ajaxListener() {
// ...
}
Nhưng trong MyFaces, nó sẽ ném a MethodNotFoundException
. Dưới đây hoạt động trong cả hai triển khai JSF khi bạn muốn bỏ qua đối số.
<h:commandXxx ...>
<f:ajax execute="@form" listener="#{bean.ajaxListener()}" render="@form" />
</h:commandXxx>
Trình nghe Ajax không thực sự hữu ích trên các thành phần lệnh. Chúng hữu ích hơn về đầu vào và chọn thành phần <h:inputXxx>
/ <h:selectXxx>
. Trong các thành phần lệnh, chỉ cần bám vào action
và / hoặc actionListener
để rõ ràng và tự viết mã tốt hơn. Hơn nữa, như actionListener
, f:ajax listener
không hỗ trợ trả về một kết quả điều hướng.
<h:commandXxx ... action="#{bean.action}">
<f:ajax execute="@form" render="@form" />
</h:commandXxx>
Để giải thích về các thuộc tính execute
và render
thuộc tính, hãy tìm hiểu quy trình / cập nhật PrimeFaces và JSF f: ajax thực thi / kết xuất các thuộc tính .
Các actionListener
s luôn gọi trước khi các action
theo thứ tự giống như họ đang được khai báo trong giao diện và gắn liền với các thành phần. Các f:ajax listener
luôn gọi trước khi bất kỳ người nghe hành động. Vì vậy, ví dụ sau:
<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>
Sẽ gọi các phương thức theo thứ tự sau:
Bean#ajaxListener()
Bean#actionListener()
ActionListenerType#processAction()
Bean#actionListenerBinding()
Bean#setProperty()
Bean#action()
Các actionListener
hỗ trợ một ngoại lệ đặc biệt : AbortProcessingException
. Nếu ngoại lệ này được ném ra từ một actionListener
phương thức, thì JSF sẽ bỏ qua mọi trình lắng nghe hành động còn lại và phương thức hành động và tiến hành kết xuất phản hồi trực tiếp. Bạn sẽ không thấy trang lỗi / ngoại lệ, tuy nhiên JSF sẽ ghi lại nó. Điều này cũng sẽ được thực hiện bất cứ khi nào bất kỳ ngoại lệ nào khác được ném ra từ một actionListener
. Vì vậy, nếu bạn có ý định chặn trang bởi một trang lỗi do ngoại lệ kinh doanh, thì bạn chắc chắn nên thực hiện công việc trong action
phương thức.
Nếu lý do duy nhất để sử dụng một actionListener
là để có một void
phương thức quay lại cùng một trang, thì đó là một phương pháp tồi. Các action
phương thức hoàn toàn cũng có thể trả về void
, trái với những gì một số IDE cho phép bạn tin tưởng thông qua xác nhận EL. Lưu ý rằng các ví dụ giới thiệu PrimeFaces nằm rải rác với loại actionListener
s này ở mọi nơi. Điều này thực sự sai. Đừng dùng cái này như một cái cớ để tự mình làm điều đó.
Tuy nhiên, trong các yêu cầu ajax, cần có một trình xử lý ngoại lệ đặc biệt. Điều này là bất kể bạn sử dụng listener
thuộc tính của <f:ajax>
hay không. Để giải thích và một ví dụ, hãy xử lý Ngoại lệ trong các yêu cầu ajax của JSF .
actionListener
, nhưng điều đó vẫn không làm cho nó trở thành một lý do tốt để lạm dụng actionListener
cho các hành động kinh doanh .
action
tương ứng với điều đó. actionListener
là cho thứ thứ. Chỉ muốn làm rõ rằng các ngoại lệ từ actionListener
s có thể được truyền bá nếu cần thiết;)
actionListener
thuộc tính và nó cũng phải public
như vậy. Các processAction
tên chỉ là bắt buộc khi bạn đang sử dụng <f:actionListener type>
, đơn giản chỉ vì các loại có để thực hiện ActionListener
giao diện trong đó có chính xác rằng tên phương pháp processAction
definied.
<f:ajax>
, trong trường hợp các thành phần lệnh thích sử dụng action
thuộc tính cho các hành động kinh doanh. Ví dụ <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>
.
Như BalusC đã chỉ ra, actionListener
mặc định nuốt các ngoại lệ, nhưng trong JSF 2.0 có thêm một chút về điều này. Cụ thể, nó không chỉ nuốt và ghi nhật ký, mà thực sự xuất bản ngoại lệ.
Điều này xảy ra thông qua một cuộc gọi như thế này:
context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,
new ExceptionQueuedEventContext(context, exception, source, phaseId)
);
Trình nghe mặc định cho sự kiện này là trình nghe ExceptionHandler
dành cho Mojarra com.sun.faces.context.ExceptionHandlerImpl
. Việc triển khai này về cơ bản sẽ lấy lại bất kỳ ngoại lệ nào, ngoại trừ khi nó liên quan đến AbortProcessingException, được ghi lại. ActionListener bao bọc ngoại lệ được ném bởi mã máy khách trong AbortProcessingException như vậy giải thích lý do tại sao chúng luôn được ghi lại.
ExceptionHandler
Tuy nhiên, điều này có thể được thay thế trong tệp fac-config.xml bằng cách triển khai tùy chỉnh:
<exception-handlerfactory>
com.foo.myExceptionHandler
</exception-handlerfactory>
Thay vì nghe toàn cầu, một hạt đậu cũng có thể nghe những sự kiện này. Sau đây là một bằng chứng về khái niệm này:
@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");
}
}
(lưu ý, đây không phải là cách người ta thường viết mã người nghe, đây chỉ là mục đích trình diễn!)
Gọi cái này từ Facelet như thế này:
<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>
Sẽ dẫn đến một trang lỗi được hiển thị.
ActionListener được kích hoạt trước, với tùy chọn sửa đổi phản hồi, trước khi Action được gọi và xác định vị trí của trang tiếp theo.
Nếu bạn có nhiều nút trên cùng một trang sẽ đi đến cùng một nơi nhưng thực hiện các thao tác hơi khác nhau, bạn có thể sử dụng cùng một Hành động cho mỗi nút, nhưng sử dụng một ActionListener khác để xử lý các chức năng hơi khác nhau.
Đây là một liên kết mô tả mối quan hệ:
TL; DR :
Các ActionListener
s (có thể có nhiều) thực thi theo thứ tự chúng đã được đăng ký TRƯỚCaction
Câu trả lời dài :
Một doanh nghiệp action
thường gọi một dịch vụ EJB và nếu cần thiết cũng đặt kết quả cuối cùng và / hoặc điều hướng đến một cái nhìn khác nhau nếu đó không phải là những gì bạn đang làm một actionListener
là tức là thích hợp hơn cho khi tương tác người dùng với các thành phần, như h:commandButton
hoặc h:link
họ có thể được xử lý bằng cách chuyển tên của phương thức bean được quản lý trong actionListener
thuộc tính của Thành phần UI hoặc để thực hiện ActionListener
giao diện và chuyển tên lớp thực hiện cho actionListener
thuộc tính của Thành phần UI.