รากฐานที่สร้างเฟรมเวิร์กที่ได้รับความนิยมมากที่สุดในขณะนี้คือ dependency insert ฉันขอแนะนำให้ดูว่าข้อกำหนดของ CDI กล่าวถึงสิ่งนี้อย่างไร ความสามารถพื้นฐานที่เรามีอยู่ และเราจะใช้มันได้อย่างไร
หลังจากนั้นสำหรับคำถาม “ เลือกหมายเลขหรือใช้ตัวกรอง ” และ “ เลือก org.apache.maven.archetypes:maven-archetype-quickstart version ” เพียงกด Enter จากนั้น ป้อนตัวระบุโปรเจ็กต์ ซึ่งเรียกว่า GAV (ดูNaming Convention Guide )
หลังจากสร้างโครงการสำเร็จแล้ว เราจะเห็นข้อความว่า "BUILD SUCCESS" ตอนนี้เราสามารถเปิดโครงการของเราใน IDE ที่เราชื่นชอบได้แล้ว
ที่นี่เราจะเห็นว่า Maven อธิบายข้อเท็จจริงที่ว่าเราใช้ CDI API ในโปรเจ็กต์อย่างไร API คืออินเทอร์เฟซการเขียนโปรแกรมแอปพลิเคชัน ซึ่งก็คืออินเทอร์เฟซการเขียนโปรแกรมบางส่วน เราทำงานร่วมกับอินเทอร์เฟซโดยไม่ต้องกังวลว่าอินเทอร์เฟซนี้ทำงานอย่างไรและทำงานอย่างไร API เป็นไฟล์เก็บถาวร jar ที่เราจะเริ่มใช้ในโปรเจ็กต์ของเรา กล่าวคือ โปรเจ็กต์ของเราเริ่มขึ้นอยู่กับ jar นี้ ดังนั้น CDI API สำหรับโปรเจ็กต์ของเราจึงขึ้นต่อกัน ใน Maven โปรเจ็กต์ได้รับการอธิบายไว้ในไฟล์ POM.xml ( POM - Project Object Model ) การขึ้นต่อกันอธิบายไว้ในบล็อกการขึ้นต่อกัน ซึ่งเราต้องเพิ่มรายการใหม่:
ทันทีที่เรามีไฟล์นี้ อาร์ติแฟกต์ที่มีโค้ดของเราจะถูกเรียกว่า " Explicit bean archive " ตอนนี้เรามีการกำหนดค่าแยกกัน 2 แบบ: ซอฟต์แวร์และ xml ปัญหาคือพวกเขาจะโหลดข้อมูลเดียวกัน ตัวอย่างเช่น คำนิยาม DataSource bean จะถูกโหลด 2 ครั้ง และโปรแกรมของเราจะเสียหายเมื่อดำเนินการ เนื่องจาก คอนเทนเนอร์ CDI จะคิดว่าพวกมันเป็น 2 bean แยกกัน (แม้ว่าในความเป็นจริงแล้วพวกมันจะเป็นคลาสเดียวกัน ซึ่งคอนเทนเนอร์ CDI เรียนรู้ประมาณสองครั้ง) เพื่อหลีกเลี่ยงปัญหานี้ มี 2 ทางเลือก:
ดังที่คุณเห็นจากรูปภาพ เรามีสิ่งที่เรียกว่าการเรียกกลับวงจรชีวิต สิ่งเหล่านี้คือคำอธิบายประกอบที่จะบอกให้คอนเทนเนอร์ CDI เรียกวิธีการบางอย่างในขั้นตอนหนึ่งของวงจรชีวิตของ bean ตัวอย่างเช่น:
การแนะนำ
ฉันอยากจะอุทิศการทบทวนสั้น ๆ นี้ให้กับ 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

การเพิ่ม CDI ให้กับโปรเจ็กต์
ในบทนำเราเห็นว่า CDI มีเว็บไซต์ที่น่าสนใจ- http://www.cdi-spec.org/ มีส่วนดาวน์โหลดซึ่งมีตารางที่มีข้อมูลที่เราต้องการ:
<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 ฉันจะเอาคุณไปไว้ที่ไหน " ดังนั้นเรามาวางไฟล์ให้ถูกต้อง:

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

GO TO FULL VERSION