Sự khác nhau giữa hành động và actionListener


Câu trả lời:


583

hành động lắng nghe

Sử dụng actionListenernế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 ActionEventtranh 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 actionListenerphươ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 bindingthuộ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ì bindingmặ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?


hoạt động

Sử dụng actionnế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 actionphương pháp có thể (do đó, không phải) trả về một Stringmà 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 nullhoặc voidsẽ 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 actionphươ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 actionthuộ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" />

Xem thêm Làm thế nào để điều hướng trong JSF? Cách tạo URL phản ánh trang hiện tại (chứ không phải trang trước) .


f: người nghe ajax

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 ajaxListenerphươ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 actionvà / hoặc actionListenerđể rõ ràng và tự viết mã tốt hơn. Hơn nữa, như actionListener, f:ajax listenerkhô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 executerenderthuộ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 .


Lệnh gọi

Các actionListeners luôn gọi trước khi các actiontheo 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 listenerluô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:

  1. Bean#ajaxListener()
  2. Bean#actionListener()
  3. ActionListenerType#processAction()
  4. Bean#actionListenerBinding()
  5. Bean#setProperty()
  6. Bean#action()

Xử lý ngoại lệ

Các actionListenerhỗ trợ một ngoại lệ đặc biệt : AbortProcessingException. Nếu ngoại lệ này được ném ra từ một actionListenerphươ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 actionphương thức.

Nếu lý do duy nhất để sử dụng một actionListenerlà để có một voidphương thức quay lại cùng một trang, thì đó là một phương pháp tồi. Các actionphươ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 actionListeners 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 listenerthuộ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 .


1
Bạn đúng rằng các ngoại lệ trong ActionListener bị nuốt theo mặc định, nhưng trong JSF 2.0, hành vi này có thể được thay đổi. Xem câu trả lời của tôi dưới đây để biết chi tiết.
Arjan Tijms

3
@arjan: Bạn nói đúng rằng JSF 2.0 cho phép bạn thay đổi cách xử lý mặc định các ngoại lệ được đưa ra 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 actionListenercho các hành động kinh doanh .
BalusC

1
Thật vậy, các hành động kinh doanh nằm trong "dòng chảy" chính của chu trình yêu cầu / phản hồi và chỉ actiontương ứng với điều đó. actionListenerlà cho thứ thứ. Chỉ muốn làm rõ rằng các ngoại lệ từ actionListeners có thể được truyền bá nếu cần thiết;)
Arjan Tijms

2
@Kawy: tên phương thức được tự do lựa chọn khi được sử dụng trong actionListenerthuộc tính và nó cũng phải publicnhư vậy. Các processActiontê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 ActionListenergiao diện trong đó có chính xác rằng tên phương pháp processActiondefinied.
BalusC

2
@Muhammed: người nghe hành động ajax được gọi trước tất cả người nghe hành động thông thường. Lưu ý rằng ngay cả khi sử dụng <f:ajax>, trong trường hợp các thành phần lệnh thích sử dụng actionthuộc tính cho các hành động kinh doanh. Ví dụ <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>.
BalusC

47

Như BalusC đã chỉ ra, actionListenermặ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 ExceptionHandlerdà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.

ExceptionHandlerTuy 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ị.


43

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ệ:

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


3
cộng với một, các chữ in đậm nói lên hầu hết mọi thứ.
Shirgill Farhan

0

TL; DR :

Các ActionListeners (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 actionthườ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 actionListenerlà 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:commandButtonhoặc h:linkhọ 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 actionListenerthuộc tính của Thành phần UI hoặc để thực hiện ActionListenergiao diện và chuyển tên lớp thực hiện cho actionListenerthuộc tính của Thành phần UI.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.