JavaRush /จาวาบล็อก /Random-TH /ทริปสั้นๆ สู่การฉีดพึ่งพาหรือ "CDI คืออะไร"
Viacheslav
ระดับ

ทริปสั้นๆ สู่การฉีดพึ่งพาหรือ "CDI คืออะไร"

เผยแพร่ในกลุ่ม
รากฐานที่สร้างเฟรมเวิร์กที่ได้รับความนิยมมากที่สุดในขณะนี้คือ dependency insert ฉันขอแนะนำให้ดูว่าข้อกำหนดของ CDI กล่าวถึงสิ่งนี้อย่างไร ความสามารถพื้นฐานที่เรามีอยู่ และเราจะใช้มันได้อย่างไร
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ

การแนะนำ

ฉันอยากจะอุทิศการทบทวนสั้น ๆ นี้ให้กับ CDI นี่คืออะไร? CDI ย่อมาจากบริบทและการฉีดพึ่งพา นี่เป็นข้อกำหนด Java EE ที่อธิบาย Dependency Injection และบริบท สำหรับข้อมูลสามารถดูได้ที่เว็บไซต์http://cdi-spec.org เนื่องจาก CDI เป็นข้อกำหนด (คำอธิบายเกี่ยวกับวิธีการทำงาน ชุดของอินเทอร์เฟซ) เราจึงจำเป็นต้องมีการนำไปปฏิบัติเพื่อใช้งานด้วย หนึ่งในการใช้งานดังกล่าวคือ Weld - http://weld.cdi-spec.org/ เพื่อจัดการการพึ่งพาและสร้างโปรเจ็กต์ เราจะใช้ Maven - https://maven.apache.org ดังนั้น เราได้ติดตั้ง Maven แล้ว ตอนนี้เรา จะเข้าใจในทางปฏิบัติเพื่อไม่ให้เข้าใจนามธรรม เพื่อที่จะทำสิ่งนี้ เราจะสร้างโปรเจ็กต์โดยใช้ Maven มาเปิดบรรทัดคำสั่ง (ใน Windows คุณสามารถใช้ Win+R เพื่อเปิดหน้าต่าง "Run" และดำเนินการ cmd) และขอให้ Maven ทำทุกอย่างให้เรา สำหรับสิ่งนี้ Maven มีแนวคิดที่เรียกว่าต้นแบบ: Maven Archetype
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ
หลังจากนั้นสำหรับคำถาม “ เลือกหมายเลขหรือใช้ตัวกรอง ” และ “ เลือก org.apache.maven.archetypes:maven-archetype-quickstart version ” เพียงกด Enter จากนั้น ป้อนตัวระบุโปรเจ็กต์ ซึ่งเรียกว่า GAV (ดูNaming Convention Guide )
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ
หลังจากสร้างโครงการสำเร็จแล้ว เราจะเห็นข้อความว่า "BUILD SUCCESS" ตอนนี้เราสามารถเปิดโครงการของเราใน IDE ที่เราชื่นชอบได้แล้ว

การเพิ่ม CDI ให้กับโปรเจ็กต์

ในบทนำเราเห็นว่า CDI มีเว็บไซต์ที่น่าสนใจ- http://www.cdi-spec.org/ มีส่วนดาวน์โหลดซึ่งมีตารางที่มีข้อมูลที่เราต้องการ:
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ
ที่นี่เราจะเห็นว่า Maven อธิบายข้อเท็จจริงที่ว่าเราใช้ CDI API ในโปรเจ็กต์อย่างไร API คืออินเทอร์เฟซการเขียนโปรแกรมแอปพลิเคชัน ซึ่งก็คืออินเทอร์เฟซการเขียนโปรแกรมบางส่วน เราทำงานร่วมกับอินเทอร์เฟซโดยไม่ต้องกังวลว่าอินเทอร์เฟซนี้ทำงานอย่างไรและทำงานอย่างไร API เป็นไฟล์เก็บถาวร jar ที่เราจะเริ่มใช้ในโปรเจ็กต์ของเรา กล่าวคือ โปรเจ็กต์ของเราเริ่มขึ้นอยู่กับ jar นี้ ดังนั้น CDI API สำหรับโปรเจ็กต์ของเราจึงขึ้นต่อกัน ใน Maven โปรเจ็กต์ได้รับการอธิบายไว้ในไฟล์ POM.xml ( POM - Project Object Model ) การขึ้นต่อกันอธิบายไว้ในบล็อกการขึ้นต่อกัน ซึ่งเราต้องเพิ่มรายการใหม่:
<dependency>
	<groupId>javax.enterprise</groupId>
	<artifactId>cdi-api</artifactId>
	<version>2.0</version>
</dependency>
ตามที่คุณอาจสังเกตเห็น เราไม่ได้ระบุขอบเขตด้วยค่าที่ให้ไว้ เหตุใดจึงมีความแตกต่างเช่นนี้? ขอบเขตนี้หมายความว่าบางคนจะให้การพึ่งพาแก่เรา เมื่อแอปพลิเคชันทำงานบนเซิร์ฟเวอร์ Java EE หมายความว่าเซิร์ฟเวอร์จะจัดเตรียมเทคโนโลยี JEE ที่จำเป็นทั้งหมดให้กับแอปพลิเคชัน เพื่อความเรียบง่ายของการตรวจสอบนี้ เราจะทำงานในสภาพแวดล้อม Java SE ดังนั้นจึงไม่มีใครมอบการพึ่งพานี้ให้กับเรา คุณสามารถอ่านเพิ่มเติมเกี่ยวกับขอบเขตการพึ่งพาได้ที่นี่: " ขอบเขตการพึ่งพา " โอเค ตอนนี้เรามีความสามารถในการทำงานกับอินเทอร์เฟซแล้ว แต่เราก็จำเป็นต้องมีการนำไปปฏิบัติด้วย อย่างที่เราจำได้ เราจะใช้ Weld เป็นเรื่องที่น่าสนใจที่มีการพึ่งพาที่แตกต่างกันออกไปทุกที่ แต่เราจะปฏิบัติตามเอกสาร ดังนั้น มาอ่าน " 18.4.5. การตั้งค่า Classpath " และทำตามที่กล่าวไว้:
<dependency>
	<groupId>org.jboss.weld.se</groupId>
	<artifactId>weld-se-core</artifactId>
	<version>3.0.5.Final</version>
</dependency>
สิ่งสำคัญคือ Weld เวอร์ชันบรรทัดที่สามรองรับ CDI 2.0 ดังนั้นเราจึงวางใจ API ของเวอร์ชันนี้ได้ ตอนนี้เราพร้อมที่จะเขียนโค้ดแล้ว
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ

กำลังเริ่มต้นคอนเทนเนอร์ CDI

CDI เป็นกลไก ต้องมีคนควบคุมกลไกนี้ ดังที่เราได้อ่านไปแล้วข้างต้น ผู้จัดการดังกล่าวก็คือคอนเทนเนอร์ ดังนั้นเราจึงจำเป็นต้องสร้างมันขึ้นมาโดยตัวมันเองจะไม่ปรากฏในสภาพแวดล้อม SE มาเพิ่มสิ่งต่อไปนี้ในวิธีการหลักของเรา:
public static void main(String[] args) {
	SeContainerInitializer initializer = SeContainerInitializer.newInstance();
	initializer.addPackages(App.class.getPackage());
	SeContainer container = initializer.initialize();
}
เราสร้างคอนเทนเนอร์ CDI ด้วยตนเองเนื่องจาก... เราทำงานในสภาพแวดล้อมแบบ SE ในโครงการการต่อสู้ทั่วไป โค้ดจะทำงานบนเซิร์ฟเวอร์ซึ่งมีเทคโนโลยีต่างๆ ให้กับโค้ด ดังนั้น หากเซิร์ฟเวอร์จัดเตรียม CDI ไว้ หมายความว่าเซิร์ฟเวอร์มีคอนเทนเนอร์ CDI อยู่แล้ว และเราไม่จำเป็นต้องเพิ่มสิ่งใดเลย แต่สำหรับวัตถุประสงค์ของบทช่วยสอนนี้ เราจะใช้สภาพแวดล้อม SE แถมตู้คอนเทนเนอร์ก็อยู่ตรงนี้ชัดเจนและเข้าใจได้ ทำไมเราต้องมีภาชนะ? ภาชนะภายในบรรจุถั่ว (ถั่ว CDI)
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ

ถั่วซีดีไอ

ดังนั้นถั่ว ถัง CDI คืออะไร? นี่คือคลาส Java ที่เป็นไปตามกฎบางอย่าง กฎเหล่านี้อธิบายไว้ในข้อกำหนดในบท " 2.2. bean เป็นคลาสประเภทใด " มาเพิ่ม CDI bean ลงในแพ็คเกจเดียวกันกับคลาส App:
public class Logger {
    public void print(String message) {
        System.out.println(message);
    }
}
ตอนนี้เราสามารถเรียก bean นี้ได้จากmainวิธีการของเรา:
Logger logger = container.select(Logger.class).get();
logger.print("Hello, World!");
อย่างที่คุณเห็น เราไม่ได้สร้าง bean โดยใช้คีย์เวิร์ดใหม่ เราถามคอนเทนเนอร์ CDI: “คอนเทนเนอร์ CDI ฉันต้องการอินสแตนซ์ของคลาส Logger จริงๆ โปรดให้ฉันด้วย” วิธีการนี้เรียกว่า " การค้นหาการพึ่งพา " นั่นคือการค้นหาการขึ้นต่อกัน ตอนนี้เรามาสร้างคลาสใหม่:
public class DateSource {
    public String getDate() {
        return new Date().toString();
    }
}
คลาสดั้งเดิมที่ส่งคืนการแสดงข้อความของวันที่ ตอนนี้เรามาเพิ่มเอาต์พุตวันที่ให้กับข้อความ:
public class Logger {
    @Inject
    private DateSource dateSource;

    public void print(String message) {
        System.out.println(dateSource.getDate() + " : " + message);
    }
}
คำอธิบายประกอบ @Inject ที่น่าสนใจปรากฏขึ้น ตามที่ระบุไว้ในบท " 4.1. จุดฉีด " ของเอกสารการเชื่อม cdi โดยใช้คำอธิบายประกอบนี้ เราจะกำหนดจุดฉีด ในภาษารัสเซีย สามารถอ่านได้ว่า "จุดนำไปปฏิบัติ" พวกมันถูกใช้โดยคอนเทนเนอร์ CDI เพื่อฉีดการขึ้นต่อกันเมื่อสร้างอินสแตนซ์ bean อย่างที่คุณเห็น เราไม่ได้กำหนดค่าใดๆ ให้กับฟิลด์ dateSource เหตุผลก็คือความจริงที่ว่าคอนเทนเนอร์ CDI อนุญาตให้ใช้ CDI bean ภายในได้ (เฉพาะถั่วที่มันสร้างอินสแตนซ์ขึ้นมาเองเท่านั้น นั่นคือมันจัดการได้) ใช้ " Dependency Injection " นี่เป็นอีกวิธีหนึ่งของInversion of Controlซึ่งเป็นแนวทางที่บุคคลอื่นควบคุมการพึ่งพาแทนที่จะสร้างวัตถุอย่างชัดเจน การฉีดการขึ้นต่อกันสามารถทำได้ผ่านวิธีการ ตัวสร้าง หรือฟิลด์ สำหรับรายละเอียดเพิ่มเติม โปรดดูบทข้อกำหนด CDI " 5.5. การพึ่งพาการฉีด " ขั้นตอนในการกำหนดสิ่งที่จำเป็นต้องดำเนินการเรียกว่าการแก้ปัญหาแบบปลอดภัย ซึ่งเป็นสิ่งที่เราต้องพูดถึง
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ

การจำแนกชื่อหรือความละเอียดของ Typesafe

โดยทั่วไปแล้ว อินเทอร์เฟซจะถูกใช้เป็นประเภทของออบเจ็กต์ที่จะนำไปใช้ และตัวคอนเทนเนอร์ CDI จะเป็นตัวกำหนดว่าจะเลือกการใช้งานแบบใด สิ่งนี้มีประโยชน์ด้วยเหตุผลหลายประการ ซึ่งเราจะหารือกัน ดังนั้นเราจึงมีอินเทอร์เฟซคนตัดไม้:
public interface Logger {
    void print(String message);
}
เขาบอกว่าถ้าเรามีคนตัดไม้ เราก็สามารถส่งข้อความไปหามันได้ และมันจะทำงานให้สำเร็จ - บันทึก ในกรณีนี้จะไม่สนใจอย่างไรและที่ไหน ตอนนี้เรามาสร้างการใช้งานสำหรับตัวบันทึกกันดีกว่า:
public class SystemOutLogger implements Logger {
    @Inject
    private DateSource dateSource;

    public void print(String message) {
        System.out.println(message);
    }
}
อย่างที่คุณเห็น นี่คือตัวบันทึกที่เขียนไปยัง System.out มหัศจรรย์. ตอนนี้วิธีการหลักของเราก็จะได้ผลเหมือนเดิม Logger logger = container.select(Logger.class).get(); คนตัดไม้จะยังคงได้รับบรรทัดนี้ และข้อดีก็คือเราเพียงแค่ต้องรู้อินเทอร์เฟซ และคอนเทนเนอร์ CDI ก็คิดเกี่ยวกับการนำไปปฏิบัติให้เราแล้ว สมมติว่าเรามีการใช้งานครั้งที่สองที่ควรส่งบันทึกไปยังที่จัดเก็บข้อมูลระยะไกล:
public class NetworkLogger implements Logger {
    @Override
    public void print(String message) {
        System.out.println("Send log message to remote log system");
    }
}
หากตอนนี้เรารันโค้ดโดยไม่มีการเปลี่ยนแปลง เราจะได้รับข้อผิดพลาดเนื่องจาก คอนเทนเนอร์ CDI เห็นการใช้งานอินเทอร์เฟซสองแบบและไม่สามารถเลือกระหว่างกันได้: org.jboss.weld.exceptions.AmbiguousResolutionException: WELD-001335: Ambiguous dependencies for type Logger จะทำอย่างไร? มีหลายรูปแบบให้เลือก สิ่งที่ง่ายที่สุดคือ คำอธิบายประกอบ @Vetoedสำหรับ CDI bean เพื่อให้คอนเทนเนอร์ CDI ไม่รับรู้ว่าคลาสนี้เป็น CDI bean แต่มีแนวทางที่น่าสนใจกว่ามาก CDI bean สามารถทำเครื่องหมายเป็น "ทางเลือก" ได้โดยใช้คำอธิบายประกอบ@Alternativeที่อธิบายไว้ในบท " 4.7. Alternatives " ของเอกสาร Weld CDI มันหมายความว่าอะไร? ซึ่งหมายความว่า เว้นแต่เราจะระบุอย่างชัดเจนว่าจะใช้มัน มันจะไม่ถูกเลือก นี่เป็นอีกทางเลือกหนึ่งของถั่ว เรามาทำเครื่องหมาย NetworkLogger bean เป็น @Alternative แล้วเราจะเห็นว่าโค้ดถูกดำเนินการอีกครั้งและถูกใช้โดย SystemOutLogger เพื่อเปิดใช้งานทาง เลือกอื่น เราต้องมี ไฟล์ beans.xml คำถามอาจเกิดขึ้น: " beans.xml ฉันจะเอาคุณไปไว้ที่ไหน " ดังนั้นเรามาวางไฟล์ให้ถูกต้อง:
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ
ทันทีที่เรามีไฟล์นี้ อาร์ติแฟกต์ที่มีโค้ดของเราจะถูกเรียกว่า " Explicit bean archive " ตอนนี้เรามีการกำหนดค่าแยกกัน 2 แบบ: ซอฟต์แวร์และ xml ปัญหาคือพวกเขาจะโหลดข้อมูลเดียวกัน ตัวอย่างเช่น คำนิยาม DataSource bean จะถูกโหลด 2 ครั้ง และโปรแกรมของเราจะเสียหายเมื่อดำเนินการ เนื่องจาก คอนเทนเนอร์ CDI จะคิดว่าพวกมันเป็น 2 bean แยกกัน (แม้ว่าในความเป็นจริงแล้วพวกมันจะเป็นคลาสเดียวกัน ซึ่งคอนเทนเนอร์ CDI เรียนรู้ประมาณสองครั้ง) เพื่อหลีกเลี่ยงปัญหานี้ มี 2 ทางเลือก:
  • ลบบรรทัดinitializer.addPackages(App.class.getPackage())และเพิ่มการบ่งชี้ทางเลือกให้กับไฟล์ xml:
<beans
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd">
    <alternatives>
        <class>ru.javarush.NetworkLogger</class>
    </alternatives>
</beans>
  • เพิ่มแอตทริบิวต์bean-discovery-modeที่มีค่า " none " ให้กับองค์ประกอบรากของ bean และระบุทางเลือกอื่นโดยทางโปรแกรม:
initializer.addPackages(App.class.getPackage());
initializer.selectAlternatives(NetworkLogger.class);
ดังนั้น เมื่อใช้ทางเลือก CDI คอนเทนเนอร์จึงสามารถกำหนดได้ว่าควรเลือกถั่วชนิดใด สิ่งที่น่าสนใจคือ ถ้าคอนเทนเนอร์ CDI รู้ทางเลือกต่างๆ มากมายสำหรับอินเทอร์เฟซเดียวกัน เราก็สามารถบอกได้โดยระบุลำดับความสำคัญโดยใช้คำอธิบายประกอบ@Priority(ตั้งแต่ CDI 1.1)
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ

รอบคัดเลือก

แยกกันเป็นเรื่องควรค่าแก่การพูดคุยเรื่องดังกล่าวในฐานะผู้คัดเลือก ตัวระบุระบุด้วยคำอธิบายประกอบเหนือ bean และปรับแต่งการค้นหา bean และตอนนี้รายละเอียดเพิ่มเติม ที่น่าสนใจคือ CDI bean ใดๆ ก็ตาม มีคุณสมบัติอย่างน้อยหนึ่งรายการ - @Any. หากเราไม่ได้ระบุตัวระบุใด ๆ เหนือ bean แต่คอนเทนเนอร์ CDI เองก็จะเพิ่ม@Anyตัวระบุอื่นให้กับตัวระบุ@Default- หากเราระบุสิ่งใด (เช่น ระบุ @Any อย่างชัดเจน) ตัวระบุ @Default จะไม่ถูกเพิ่มโดยอัตโนมัติ แต่ข้อดีของรอบคัดเลือกคือคุณสามารถสร้างรอบคัดเลือกของคุณเองได้ ตัวระบุแทบไม่ต่างจากคำอธิบายประกอบเพราะว่า โดยพื้นฐานแล้วนี่เป็นเพียงคำอธิบายประกอบที่เขียนด้วยวิธีพิเศษ ตัวอย่างเช่น คุณสามารถป้อน Enum สำหรับประเภทโปรโตคอล:
public enum ProtocolType {
    HTTP, HTTPS
}
ต่อไปเราสามารถสร้างรอบคัดเลือกที่จะคำนึงถึงประเภทนี้:
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Protocol {
    ProtocolType value();
    @Nonbinding String comment() default "";
}
เป็นที่น่าสังเกตว่าช่องที่ทำเครื่องหมายว่า@Nonbindingไม่ส่งผลต่อการพิจารณาคัดเลือก ตอนนี้คุณต้องระบุตัวระบุ โดยจะระบุไว้เหนือประเภท bean (เพื่อให้ CDI รู้วิธีกำหนด) และเหนือจุดฉีด (พร้อมคำอธิบายประกอบ @Inject เพื่อให้คุณเข้าใจว่าควรมองหา bean ใดสำหรับการฉีดในสถานที่นี้) ตัวอย่างเช่น เราสามารถเพิ่มคลาสที่มีตัวระบุได้ เพื่อความง่าย สำหรับบทความนี้ เราจะดำเนินการภายใน NetworkLogger:
public interface Sender {
	void send(byte[] data);
}

@Protocol(ProtocolType.HTTP)
public static class HTTPSender implements Sender{
	public void send(byte[] data) {
		System.out.println("sended via HTTP");
	}
}

@Protocol(ProtocolType.HTTPS)
public static class HTTPSSender implements Sender{
	public void send(byte[] data) {
		System.out.println("sended via HTTPS");
	}
}
จากนั้นเมื่อเราทำ Inject เราจะระบุตัวระบุที่จะกำหนดคลาสที่จะใช้:
@Inject
@Protocol(ProtocolType.HTTPS)
private Sender sender;
เยี่ยมเลยใช่มั้ยล่ะ?) ดูเหมือนสวย แต่ก็ไม่รู้ว่าทำไม ทีนี้ลองจินตนาการถึงสิ่งต่อไปนี้:
Protocol protocol = new Protocol() {
	@Override
	public Class<? extends Annotation> annotationType() {
		return Protocol.class;
	}
	@Override
	public ProtocolType value() {
		String value = "HTTP";
		return ProtocolType.valueOf(value);
	}
};
container.select(NetworkLogger.Sender.class, protocol).get().send(null);
วิธีนี้เราสามารถแทนที่การรับค่าเพื่อให้สามารถคำนวณแบบไดนามิกได้ ตัวอย่างเช่น สามารถนำมาจากการตั้งค่าบางอย่างได้ จากนั้นเราสามารถเปลี่ยนการใช้งานได้ทันที โดยไม่ต้องคอมไพล์ใหม่หรือรีสตาร์ทโปรแกรม/เซิร์ฟเวอร์ มันน่าสนใจมากขึ้นใช่ไหม? )
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ

ผู้ผลิต

คุณสมบัติที่มีประโยชน์อีกประการหนึ่งของ CDI คือผู้ผลิต นี่เป็นวิธีการพิเศษ (ทำเครื่องหมายด้วยคำอธิบายประกอบพิเศษ) ที่ถูกเรียกเมื่อ bean บางตัวร้องขอการพึ่งพาการฉีด รายละเอียดเพิ่มเติมอธิบายไว้ในเอกสารประกอบในส่วน " 2.2.3 วิธีการของผู้ผลิต " ตัวอย่างที่ง่ายที่สุด:
@Produces
public Integer getRandomNumber() {
	return new Random().nextInt(100);
}
ตอนนี้เมื่อทำการฉีดเข้าไปในฟิลด์ประเภท Integer วิธีการนี้จะถูกเรียกและจะได้รับค่าจากมัน ที่นี่เราควรเข้าใจทันทีว่าเมื่อเราเห็นคำหลักใหม่ เราต้องเข้าใจทันทีว่านี่ไม่ใช่ CDI bean นั่นคือ อินสแตนซ์ของคลาส Random จะไม่กลายเป็น CDI bean เพียงเพราะมันได้มาจากสิ่งที่ควบคุมคอนเทนเนอร์ CDI (ในกรณีนี้คือตัวสร้าง)
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ

เครื่องสกัดกั้น

Interceptors คือ Interceptors ที่ “รบกวน” การทำงาน ใน CDI สิ่งนี้ทำได้ค่อนข้างชัดเจน มาดูกันว่าเราจะบันทึกโดยใช้ล่าม (หรือตัวดัก) ได้อย่างไร ขั้นแรก เราต้องอธิบายการเชื่อมโยงกับตัวสกัดกั้นก่อน เช่นเดียวกับหลายๆ สิ่ง การดำเนินการนี้ทำได้โดยใช้คำอธิบายประกอบ:
@Inherited
@InterceptorBinding
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface ConsoleLog {
}
สิ่งสำคัญที่นี่คือการเชื่อมโยงสำหรับ interceptor ( @InterceptorBinding) ซึ่งจะได้รับการสืบทอดโดยการขยาย ( @InterceptorBinding) ตอนนี้เรามาเขียนตัวสกัดกั้นเอง:
@Interceptor
@ConsoleLog
public class LogInterceptor {
    @AroundInvoke
    public Object log(InvocationContext ic) throws Exception {
        System.out.println("Invocation method: " + ic.getMethod().getName());
        return ic.proceed();
    }
}
คุณสามารถอ่านเพิ่มเติมเกี่ยวกับวิธีการเขียน Interceptor ได้ในตัวอย่างจากข้อกำหนด: " 1.3.6. ตัวอย่าง Interceptor " สิ่งที่เราต้องทำคือเปิดอินเนอร์เซปเตอร์ เมื่อต้องการทำเช่นนี้ ให้ระบุคำอธิบายประกอบการโยงเหนือวิธีการที่กำลังดำเนินการ:
@ConsoleLog
public void print(String message) {
และตอนนี้รายละเอียดที่สำคัญอีกอย่างหนึ่ง ระบบดักฟังจะถูกปิดใช้งานตามค่าเริ่มต้น และจะต้องเปิดใช้งานในลักษณะเดียวกับทางเลือกอื่น ตัวอย่างเช่น ใน ไฟล์ beans.xml :
<interceptors>
	<class>ru.javarush.LogInterceptor</class>
</interceptors>
อย่างที่คุณเห็นมันค่อนข้างง่าย
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ

เหตุการณ์และผู้สังเกตการณ์

CDI ยังจัดทำแบบจำลองเหตุการณ์และผู้สังเกตการณ์ด้วย ที่นี่ทุกอย่างไม่ชัดเจนเท่ากับเครื่องสกัดกั้น ดังนั้น เหตุการณ์ในกรณีนี้สามารถเป็นคลาสใดก็ได้ ไม่จำเป็นต้องมีอะไรพิเศษสำหรับคำอธิบาย ตัวอย่างเช่น:
public class LogEvent {
    Date date = new Date();
    public String getDate() {
        return date.toString();
    }
}
ตอนนี้มีคนควรรอเหตุการณ์:
public class LogEventListener {
    public void logEvent(@Observes LogEvent event){
        System.out.println("Message Date: " + event.getDate());
    }
}
สิ่งสำคัญที่นี่คือการระบุคำอธิบายประกอบ @Observes ซึ่งบ่งชี้ว่านี่ไม่ใช่แค่วิธีการ แต่เป็นวิธีการที่ควรเรียกใช้เนื่องจากการสังเกตเหตุการณ์ประเภท LogEvent ตอนนี้เราต้องการคนที่จะดู:
public class LogObserver {
    @Inject
    private Event<LogEvent> event;
    public void observe(LogEvent logEvent) {
        event.fire(logEvent);
    }
}
เรามีวิธีเดียวที่จะบอกคอนเทนเนอร์ว่ามีเหตุการณ์เหตุการณ์เกิดขึ้นสำหรับประเภทเหตุการณ์ LogEvent ตอนนี้สิ่งที่เหลืออยู่คือการใช้ผู้สังเกตการณ์ ตัวอย่างเช่น ใน NetworkLogger เราสามารถเพิ่มการแทรกผู้สังเกตการณ์ของเราได้:
@Inject
private LogObserver observer;
และในวิธีการพิมพ์เราสามารถแจ้งผู้สังเกตการณ์ได้ว่าเรามีเหตุการณ์ใหม่:
public void print(String message) {
	observer.observe(new LogEvent());
สิ่งสำคัญคือต้องรู้ว่าเหตุการณ์สามารถประมวลผลได้ในเธรดเดียวหรือหลายเธรด สำหรับการประมวลผลแบบอะซิงโครนัส ให้ใช้วิธีการ.fireAsync(แทน .fire) และคำอธิบายประกอบ@ObservesAsync(แทน @Observes) ตัวอย่างเช่น หากเหตุการณ์ทั้งหมดถูกดำเนินการในเธรดที่แตกต่างกัน หาก 1 เธรดส่งข้อยกเว้น เธรดอื่นๆ จะสามารถทำงานของตนสำหรับเหตุการณ์อื่นได้ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับเหตุการณ์ใน CDI ได้ตามปกติในข้อกำหนดในบท " 10. เหตุการณ์ "
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ

ช่างตกแต่ง

ดังที่เราเห็นข้างต้น รูปแบบการออกแบบต่างๆ จะถูกรวบรวมไว้ภายใต้ปีก CDI และนี่คืออีกคนหนึ่ง - มัณฑนากร นี่เป็นสิ่งที่น่าสนใจมาก มาดูคลาสนี้กัน:
@Decorator
public abstract class LoggerDecorator implements Logger {
    public final static String ANSI_GREEN = "\u001B[32m";
    public static final String ANSI_RESET = "\u001B[0m";

    @Inject
    @Delegate
    private Logger delegate;

    @Override
    public void print(String message) {
        delegate.print(ANSI_GREEN + message + ANSI_RESET);
    }
}
ด้วยการประกาศว่าเป็นมัณฑนากร เราบอกว่าเมื่อมีการใช้ Logger ใดๆ “ส่วนเสริม” นี้จะถูกใช้งาน ซึ่งรู้การใช้งานจริง ซึ่งถูกจัดเก็บไว้ในฟิลด์ผู้รับมอบสิทธิ์ (เนื่องจากมีการทำเครื่องหมายด้วยคำอธิบายประกอบ@Delegate) ผู้ตกแต่งสามารถเชื่อมโยงกับ CDI bean เท่านั้น ซึ่งตัวมันเองไม่ใช่ทั้งผู้สกัดกั้นหรือผู้ตกแต่ง ตัวอย่างสามารถดูได้ในข้อกำหนด: " 1.3.7 ตัวอย่างมัณฑนากร " ต้องเปิดมัณฑนากรเช่นเดียวกับเครื่องสกัดกั้น ตัวอย่างเช่น ในbeans.xml :
<decorators>
	<class>ru.javarush.LoggerDecorator</class>
</decorators>
สำหรับรายละเอียดเพิ่มเติม โปรดดูเอกสารอ้างอิงการเชื่อม: " บทที่ 10. อุปกรณ์ตกแต่ง "

วงจรชีวิต

ถั่วมีวงจรชีวิตของตัวเอง มีลักษณะดังนี้:
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ
ดังที่คุณเห็นจากรูปภาพ เรามีสิ่งที่เรียกว่าการเรียกกลับวงจรชีวิต สิ่งเหล่านี้คือคำอธิบายประกอบที่จะบอกให้คอนเทนเนอร์ CDI เรียกวิธีการบางอย่างในขั้นตอนหนึ่งของวงจรชีวิตของ bean ตัวอย่างเช่น:
@PostConstruct
public void init() {
	System.out.println("Inited");
}
วิธีการนี้จะถูกเรียกเมื่อ CDI bean ถูกสร้างอินสแตนซ์โดยคอนเทนเนอร์ สิ่งเดียวกันนี้จะเกิดขึ้นกับ @PreDestroy เมื่อ bean ถูกทำลายเมื่อไม่ต้องการอีกต่อไป ไม่ใช่เพื่ออะไรเลยที่ตัวย่อ CDI มีตัวอักษร C - บริบท Beans ใน CDI เป็นแบบบริบท ซึ่งหมายความว่าวงจรชีวิตของถั่วนั้นขึ้นอยู่กับบริบทที่มีอยู่ภายในคอนเทนเนอร์ CDI เพื่อให้เข้าใจสิ่งนี้ได้ดีขึ้น คุณควรอ่านส่วนข้อกำหนด “ 7. วงจรการใช้งานของอินสแตนซ์ตามบริบท ” นอกจากนี้ การทราบด้วยว่าตัวคอนเทนเนอร์นั้นมีวงจรการใช้งาน ซึ่งคุณสามารถอ่านรายละเอียดได้ใน “ เหตุการณ์วงจรการใช้งานคอนเทนเนอร์
การเดินทางสั้น ๆ ไปสู่การฉีดพึ่งพาหรือ

ทั้งหมด

ด้านบนเรามองไปที่ส่วนปลายสุดของภูเขาน้ำแข็งที่เรียกว่า CDI CDI เป็นส่วนหนึ่งของข้อกำหนด JEE และใช้ในสภาพแวดล้อม JavaEE ผู้ที่ใช้ Spring ไม่ได้ใช้ CDI แต่เป็น DI นั่นคือข้อกำหนดเหล่านี้แตกต่างกันเล็กน้อย แต่เมื่อรู้และเข้าใจสิ่งที่กล่าวมาข้างต้นแล้ว คุณจะเปลี่ยนใจได้ง่ายๆ เมื่อพิจารณาว่า Spring รองรับคำอธิบายประกอบจากโลก CDI (Inject เดียวกัน) วัสดุเพิ่มเติม: #เวียเชสลาฟ
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION