ความแตกต่างระหว่าง action และ actionListener


คำตอบ:


583

ActionListener

ใช้actionListenerหากคุณต้องการให้มี hook ก่อนที่การดำเนินการทางธุรกิจจริงจะถูกดำเนินการเช่นเพื่อบันทึกและ / หรือเพื่อตั้งค่าคุณสมบัติเพิ่มเติม (โดย<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 จะโยน a 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จะให้มันกลับไปที่หน้าเดียวกันและทำให้ขอบเขตมุมมองปัจจุบันยังคงอยู่ ค่าส่งคืนของสตริงว่างหรือ ID มุมมองเดียวกันจะกลับไปยังหน้าเดียวกัน แต่สร้างขอบเขตมุมมองใหม่และทำลายถั่วที่มีมุมมองที่แอ็คทีฟอยู่ในปัจจุบันและสร้างถ้าทำได้

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";
}

ให้ใส่สตริง hardcoded ลงในแอตทริบิวต์โดยตรงแทน

<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 ที่สะท้อนให้เห็นถึงหน้าปัจจุบัน (และไม่ได้ก่อนหน้านี้อย่างใดอย่างหนึ่ง)


f: ผู้ฟังอาแจ็กซ์

ตั้งแต่ 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) {
    // ...
}

ใน Mojarra 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: อาแจ็กซ์รัน / ทำให้คุณลักษณะ


คำสั่งขอร้อง

actionListeners จะเรียกเสมอก่อนที่จะ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>

จะเรียกใช้เมธอดตามลำดับต่อไปนี้:

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

การจัดการข้อยกเว้น

สนับสนุนข้อยกเว้นพิเศษ:actionListener AbortProcessingExceptionหากข้อยกเว้นนี้ถูกโยนออกมาจากactionListenerวิธีการแล้ว JSF จะข้ามตัวรับฟังการดำเนินการใด ๆ ที่เหลืออยู่และวิธีการดำเนินการและดำเนินการเพื่อแสดงการตอบสนองโดยตรง คุณจะไม่เห็นหน้าข้อผิดพลาด / ข้อยกเว้น JSF จะทำการบันทึก นี้จะทำได้โดยปริยายเมื่อใดก็ตามที่ยกเว้นอื่น ๆ actionListenerที่จะถูกโยนลงมาจาก ดังนั้นหากคุณตั้งใจจะปิดกั้นเพจด้วยหน้าข้อผิดพลาดอันเป็นผลมาจากข้อยกเว้นทางธุรกิจคุณควรดำเนินการตามactionวิธีที่แน่นอน

หากเหตุผลเดียวที่ใช้actionListenerคือให้มีvoidวิธีการกลับไปที่หน้าเดียวกันนั่นเป็นเหตุผลที่ไม่ดี actionวิธีการที่ดีที่สุดที่สามารถยังกลับvoidในทางตรงกันข้ามกับสิ่งที่ IDEs บางให้คุณเชื่อว่าผ่านการตรวจสอบ EL โปรดทราบว่าตัวอย่างการแสดง PrimeFacesจะทิ้งกระจุยกระจายกับประเภทนี้actionListenerทั่วทุกสถานที่ นี่เป็นสิ่งที่ผิดแน่นอน อย่าใช้สิ่งนี้เป็นข้อแก้ตัวในการทำเช่นนั้นด้วยตัวเอง

อย่างไรก็ตามในคำร้องขอ ajax ต้องการตัวจัดการข้อยกเว้นพิเศษ สิ่งนี้ไม่ว่าคุณจะใช้listenerคุณลักษณะของ<f:ajax>หรือไม่ก็ตาม สำหรับคำอธิบายและตัวอย่างหัวเพื่อการจัดการข้อยกเว้นใน JSF คำขออาแจ็กซ์


1
คุณพูดถูกว่าข้อยกเว้นใน actionListeners ถูกกลืนกินโดยค่าเริ่มต้น แต่ใน JSF 2.0 ลักษณะการทำงานนี้สามารถเปลี่ยนแปลงได้ ดูคำตอบของฉันด้านล่างสำหรับรายละเอียด
Arjan Tijms

3
@arjan: คุณถูกต้องที่ JSF 2.0 อนุญาตให้คุณเปลี่ยนการจัดการเริ่มต้นของข้อยกเว้นที่โยนโดยactionListenerแต่ก็ยังไม่ได้ทำให้มันเป็นข้อแก้ตัวที่ดีactionListenerสำหรับการกระทำที่ผิดทางธุรกิจ
BalusC

1
แท้จริงแล้วการกระทำทางธุรกิจอยู่ใน "การไหล" หลักของวงจรคำขอ / การตอบสนองและมีเพียงสิ่งที่actionสอดคล้องกับสิ่งนั้น actionListenerสำหรับสิ่งของรอง แค่อยากจะชี้แจงว่าข้อยกเว้นจากactionListeners สามารถแพร่กระจายถ้าจำเป็นต้องใช้เพื่อให้;)
Arjan Tijms

2
@Kawy: ชื่อวิธีการมีอิสระที่จะเลือกเมื่อใช้ในactionListenerแอตทริบิวต์และจะต้องมีpublicเช่นกัน processActionชื่อมีผลบังคับใช้เฉพาะเมื่อคุณกำลังใช้<f:actionListener type>เพียงเพราะประเภทที่มีการใช้ActionListenerอินเตอร์เฟซที่มีชื่อตรงที่วิธีการprocessActionที่กำหนดโดย
BalusC

2
@Muhammed: ผู้ฟังการกระทำ ajax ถูกเรียกใช้ก่อนที่ผู้ฟังการกระทำปกติทุกคน โปรดทราบว่าแม้ในขณะที่ใช้<f:ajax>งานคุณจะต้องใช้actionแอตทริบิวต์คำสั่งสำหรับการดำเนินธุรกิจ <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>เช่น
BalusC

47

ดังที่ BalusC ระบุไว้actionListenerโดยค่าเริ่มต้นจะทำการยกเว้นข้อยกเว้น แต่ใน JSF 2.0 จะมีข้อผิดพลาดเล็กน้อย กล่าวคือมันไม่เพียง แต่กลืนและบันทึก แต่จะเผยแพร่ข้อยกเว้น

สิ่งนี้เกิดขึ้นผ่านการโทรเช่นนี้:

context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,                                                          
    new ExceptionQueuedEventContext(context, exception, source, phaseId)
);

ฟังเริ่มต้นสำหรับเหตุการณ์นี้เป็นที่สำหรับกระโดงมีการตั้งค่าExceptionHandler com.sun.faces.context.ExceptionHandlerImplการใช้งานนี้โดยทั่วไปจะ rethrow ข้อยกเว้นใด ๆ ยกเว้นเมื่อมันเกี่ยวข้องกับ AbortProcessingException ซึ่งถูกบันทึกไว้ ActionListeners จะตัดข้อยกเว้นที่เกิดขึ้นจากรหัสลูกค้าใน AbortProcessingException ที่อธิบายว่าทำไมสิ่งเหล่านี้ถึงถูกบันทึกไว้เสมอ

นี้ExceptionHandlerสามารถเปลี่ยนได้ แต่ในใบหน้า-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>

จะทำให้หน้าแสดงข้อผิดพลาดปรากฏขึ้น


43

ActionListener เริ่มทำงานก่อนโดยมีตัวเลือกในการแก้ไขการตอบกลับก่อนที่ Action จะถูกเรียกใช้และกำหนดตำแหน่งของหน้าถัดไป

หากคุณมีหลายปุ่มในหน้าเดียวกันซึ่งควรไปยังสถานที่เดียวกัน แต่ทำสิ่งที่แตกต่างออกไปเล็กน้อยคุณสามารถใช้ Action เดียวกันสำหรับแต่ละปุ่ม แต่ใช้ ActionListener ที่แตกต่างกันเพื่อจัดการฟังก์ชันการทำงานที่แตกต่างกันเล็กน้อย

นี่คือลิงค์ที่อธิบายความสัมพันธ์:

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


3
บวกหนึ่งตัวอักษรตัวหนาพูดเกือบทุกอย่าง
Shirgill Farhan

0

TL; DR :

ActionListeners (สามารถมีได้หลาย) ดำเนินการเพื่อที่พวกเขาได้รับการจดทะเบียนก่อนaction

คำตอบยาว :

actionโดยทั่วไปธุรกิจจะเรียกใช้บริการ EJB และหากจำเป็นต้องตั้งค่าผลลัพธ์สุดท้ายและ / หรือนำทางไปยังมุมมองที่แตกactionListenerต่างหากนั่นไม่ใช่สิ่งที่คุณกำลังทำอยู่ก็จะเหมาะสมกว่าเช่นเมื่อผู้ใช้โต้ตอบกับส่วนประกอบเช่นh:commandButtonหรือh:linkสามารถ ได้รับการจัดการโดยส่งชื่อของวิธี bean ที่ได้รับการจัดการในactionListenerแอตทริบิวต์ของ UI Component หรือเพื่อนำActionListenerอินเตอร์เฟสไปใช้และส่งผ่านชื่อคลาสการนำไปใช้งานกับactionListenerแอตทริบิวต์ของ Component UI

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.