หนังสือของ Dr. Yan Hong เรื่อง "JAVA and Patterns" เริ่มต้นด้วยคำอธิบายของโมเดล Chain of Responsibility:
รูปแบบห่วงโซ่ความรับผิดชอบคือรูปแบบพฤติกรรมของวัตถุ ในรูปแบบสายโซ่แห่งความรับผิดชอบ วัตถุจำนวนมากเชื่อมต่อกันเพื่อสร้างสายโซ่โดยการอ้างอิงของวัตถุแต่ละชิ้นไปยังลูกหลานของมัน คำขอจะถูกส่งต่อไปยังห่วงโซ่จนกว่าวัตถุในห่วงโซ่จะตัดสินใจจัดการคำขอ ลูกค้าที่ทำการร้องขอไม่ทราบว่าออบเจ็กต์ใดในสายโซ่ที่จะจัดการคำขอในท้ายที่สุด ซึ่งช่วยให้ระบบจัดระเบียบใหม่และกำหนดความรับผิดชอบแบบไดนามิกได้โดยไม่กระทบต่อลูกค้า
เริ่มจากตีกลองและโปรยดอกไม้
การตีกลองและส่งดอกไม้เป็นเกมการดื่มที่มีชีวิตชีวาและตึงเครียด ในระหว่างงานเลี้ยง แขกจะนั่งตามลำดับ และคนหนึ่งตีกลอง โดยแยกสถานที่ตีกลองและสถานที่ที่ส่งดอกไม้เพื่อแสดงความเป็นธรรม เมื่อการตีกลองเริ่มขึ้น ช่อดอกไม้จะเริ่มถูกส่งต่อ และทันทีที่กลองหล่นลง ถ้าช่อดอกไม้อยู่ในมือของใครบางคน คนนั้นจะต้องดื่ม
ตัวอย่างเช่น Jia Mu, Jia She, Jia Zheng, Jia Baoyu และ Jia Huan เป็นผู้สัญจรดอกไม้ห้าคนที่เข้าร่วมในเกมตีกลองและส่งดอกไม้ พวกเขารวมตัวกันเป็นลูกโซ่ มือกลองส่งดอกไม้ให้ Jia Mu และเริ่มเกมส่งดอกไม้ ดอกไม้ถูกส่งผ่านจาก Jia Mu ไปยัง Jia She จาก Jia She ไปยัง Jia Zheng จาก Jia Zheng ไปยัง Jia Baoyu จาก Jia Baoyu ถึง Jia Huan จาก Jia Huan ไปยัง Jia Mu และอื่นๆ ดังแสดงในรูปด้านล่าง เมื่อเสียงกลองหยุดลง ผู้ที่มีดอกไม้อยู่ในมือจะต้องสั่งเครื่องดื่ม
การตีกลองและโปรยดอกไม้เป็นการใช้รูปแบบห่วงโซ่ความรับผิดชอบ สายโซ่ความรับผิดชอบอาจเป็นเส้นตรง สายโซ่ หรือส่วนหนึ่งของโครงสร้างต้นไม้
โครงสร้างของแบบจำลองห่วงโซ่ความรับผิดชอบ
การใช้รูปแบบสายโซ่ความรับผิดชอบที่ง่ายที่สุดมีดังต่อไปนี้
บทบาทที่เกี่ยวข้องกับรูปแบบห่วงโซ่ความรับผิดชอบมีดังนี้:
● บทบาทตัวจัดการบทคัดย่อ (ตัวจัดการ): กำหนดอินเทอร์เฟซสำหรับการประมวลผลคำขอ หากจำเป็น อินเทอร์เฟซสามารถกำหนดวิธีการตั้งค่าและส่งคืนการอ้างอิงไปยังอินเทอร์เฟซถัดไป โดยปกติบทบาทนี้จะถูกนำไปใช้โดยคลาสนามธรรมของ Java หรืออินเทอร์เฟซ Java ความสัมพันธ์แบบรวมของคลาส Handler ในรูปด้านบนให้การอ้างอิงของคลาสย่อยเฉพาะไปยังคลาสถัดไป เมธอด abstract handleRequest() สร้างมาตรฐานการทำงานของคลาสย่อยในการประมวลผลคำขอ
● บทบาทตัวจัดการคอนกรีต (ConcreteHandler) หลังจากได้รับคำขอแล้ว ตัวจัดการคอนกรีตสามารถเลือกที่จะดำเนินการตามคำขอหรือส่งคำขอไปยังฝ่ายถัดไปได้ เนื่องจากตัวประมวลผลคอนกรีตมีการอ้างอิงถึงบ้านหลังถัดไป ตัวประมวลผลคอนกรีตจึงสามารถเข้าถึงบ้านหลังถัดไปได้หากจำเป็น
ซอร์สโค้ด
บทบาทตัวจัดการที่เป็นนามธรรม
คัดลอกรหัสรหัสดังต่อไปนี้:
ตัวจัดการคลาสนามธรรมสาธารณะ {
-
* ถือวัตถุความรับผิดชอบของผู้สืบทอด
-
ผู้สืบทอดตัวจัดการที่ได้รับการคุ้มครอง
-
* ระบุวิธีการประมวลผลคำขอ แม้ว่าวิธีนี้จะไม่ส่งผ่านพารามิเตอร์ก็ตาม
* อย่างไรก็ตาม สามารถส่งพารามิเตอร์ได้จริง คุณสามารถเลือกว่าจะส่งพารามิเตอร์ตามความต้องการเฉพาะหรือไม่
-
โมฆะนามธรรมสาธารณะ handleRequest();
-
* วิธีค่า
-
ตัวจัดการสาธารณะ getSuccessor () {
ผู้สืบทอดกลับมา;
-
-
* วิธีการมอบหมายเพื่อกำหนดวัตถุความรับผิดชอบในภายหลัง
-
โมฆะสาธารณะ setSuccessor (ผู้สืบทอดตัวจัดการ) {
this.successor = ผู้สืบทอด;
-
-
บทบาทตัวประมวลผลเฉพาะ
คัดลอกรหัสรหัสดังต่อไปนี้:
ConcreteHandler ระดับสาธารณะขยายตัวจัดการ {
-
* วิธีการประมวลผล เรียกวิธีนี้เพื่อประมวลผลคำขอ
-
@แทนที่
โมฆะสาธารณะ handleRequest () {
-
* ตรวจสอบว่ามีวัตถุที่รับผิดชอบผู้สืบทอดหรือไม่
* หากมีให้ส่งต่อคำขอไปยังวัตถุที่รับผิดชอบในภายหลัง
* หากไม่เป็นเช่นนั้น ให้ดำเนินการตามคำขอ
-
ถ้า(getSuccessor() != null)
-
System.out.println("ปล่อยคำขอไป");
getSuccessor().handleRequest();
}อื่น
-
System.out.println("กำลังประมวลผลคำขอ");
-
-
-
คลาสลูกค้า
คัดลอกรหัสรหัสดังต่อไปนี้:
ลูกค้าคลาสสาธารณะ {
โมฆะสาธารณะคงหลัก (สตริง [] args) {
//รวบรวมห่วงโซ่ความรับผิดชอบ
ตัวจัดการ handler1 = ConcreteHandler ใหม่ ();
ตัวจัดการ handler2 = ConcreteHandler ใหม่ ();
handler1.setSuccessor (ตัวจัดการ 2);
//ยื่นคำร้อง
handler1.handleRequest ();
-
-
จะเห็นได้ว่าไคลเอ็นต์สร้างอ็อบเจ็กต์ตัวจัดการสองตัว และระบุว่าอ็อบเจ็กต์ตัวจัดการถัดไปของออบเจ็กต์ตัวจัดการแรกคือออบเจ็กต์ตัวจัดการที่สอง ในขณะที่ออบเจ็กต์ตัวจัดการที่สองไม่มีโฮมถัดไป ไคลเอนต์ผ่านการร้องขอไปยังวัตถุตัวจัดการแรก
เนื่องจากตรรกะในการถ่ายโอนของตัวอย่างนี้ง่ายมาก: ตราบใดที่มีเจ้าของคนต่อไป มันจะถูกส่งต่อไปยังเจ้าของคนถัดไปเพื่อการประมวลผล หากไม่มีเจ้าของคนต่อไป มันจะถูกจัดการด้วยตัวเอง ดังนั้น หลังจากที่ออบเจ็กต์ตัวจัดการแรกได้รับการร้องขอ มันจะผ่านการร้องขอไปยังออบเจ็กต์ตัวจัดการตัวที่สอง เนื่องจากออบเจ็กต์ตัวจัดการตัวที่สองไม่มีที่อยู่ จึงจัดการคำขอได้ด้วยตัวเอง แผนภาพลำดับกิจกรรมแสดงอยู่ด้านล่าง
สถานการณ์การใช้งาน
ลองพิจารณาฟังก์ชั่นดังกล่าว: การสมัครเพื่อจัดการค่าใช้จ่ายในการเลี้ยงอาหารค่ำ
บริษัทหลายแห่งมีสิทธิประโยชน์ประเภทนี้ กล่าวคือ ทีมงานโครงการหรือแผนกสามารถขอค่าใช้จ่ายงานเลี้ยงอาหารค่ำบางส่วนจากบริษัทได้ซึ่งใช้ในการจัดงานเลี้ยงอาหารค่ำให้กับสมาชิกทีมงานโครงการหรือสมาชิกแผนก
ขั้นตอนทั่วไปในการสมัครค่าใช้จ่ายงานเลี้ยงอาหารค่ำโดยทั่วไปคือ ผู้สมัครกรอกแบบฟอร์มใบสมัครก่อนแล้วจึงส่งให้หัวหน้าอนุมัติ หากใบสมัครได้รับการอนุมัติ หัวหน้าจะแจ้งให้ผู้สมัครทราบว่าผ่านการอนุมัติแล้ว แล้วผู้สมัครจะไปที่ฝ่ายการเงินเพื่อเก็บค่าธรรมเนียม หากไม่อนุมัติ หัวหน้าจะแจ้งว่าไม่ผ่านการอนุมัติและเรื่องจะถูกยกเลิก
ผู้นำในระดับต่างๆ มีขีดจำกัดการอนุมัติที่แตกต่างกัน ตัวอย่างเช่น ผู้จัดการโครงการสามารถอนุมัติใบสมัครได้ในวงเงิน 500 หยวนเท่านั้น ผู้จัดการแผนกสามารถอนุมัติใบสมัครด้วยวงเงิน 1,000 หยวนเท่าใดก็ได้
กล่าวอีกนัยหนึ่งเมื่อมีคนร้องขอค่าใช้จ่ายงานเลี้ยงอาหารค่ำคำขอจะได้รับการดำเนินการตามนั้นโดยผู้จัดการโครงการผู้จัดการแผนกและผู้จัดการทั่วไปคนใดคนหนึ่ง แต่ผู้ที่ร้องขอไม่รู้ว่าสุดท้ายจะเกิดอะไรขึ้น ใครจะจัดการคำขอของเขา โดยทั่วไป ผู้สมัครจะส่งใบสมัครของเขาไปยังผู้จัดการโครงการ และในที่สุด ผู้จัดการทั่วไปก็จะจัดการคำขอของเขา
คุณสามารถใช้แบบจำลองสายโซ่ความรับผิดชอบเพื่อตระหนักถึงฟังก์ชันข้างต้น: เมื่อมีคนร้องขอค่าใช้จ่ายงานเลี้ยงอาหารค่ำ คำขอจะถูกส่งต่อในห่วงโซ่การประมวลผลความเป็นผู้นำ เช่น ผู้จัดการโครงการ -> ผู้จัดการแผนก -> ผู้จัดการทั่วไป ผู้ร้องขอไม่รู้ว่าใครจะจัดการคำขอของตน ผู้นำแต่ละคนจะตัดสินว่าจะจัดการคำขอหรือส่งมอบให้กับผู้นำระดับสูงขึ้นตามขอบเขตความรับผิดชอบของตนเอง ตราบใดที่ผู้นำจัดการ การโอนสิ้นสุดลงแล้ว
จำเป็นต้องแยกการประมวลผลของผู้นำแต่ละคนและนำไปใช้ในออบเจ็กต์การประมวลผลความรับผิดชอบที่แยกจากกัน จากนั้นจัดเตรียมออบเจ็กต์ความรับผิดชอบหลักทั่วไปที่เป็นนามธรรม เพื่อให้สามารถรวมสายโซ่ความรับผิดชอบแบบไดนามิกบนไคลเอนต์เพื่อให้บรรลุข้อกำหนดการทำงานที่แตกต่างกัน .
ซอร์สโค้ด
คลาสบทบาทตัวจัดการแบบนามธรรม
คัดลอกรหัสรหัสดังต่อไปนี้:
ตัวจัดการคลาสนามธรรมสาธารณะ {
-
* เก็บวัตถุที่จัดการคำขอถัดไป
-
ผู้สืบทอดตัวจัดการที่ได้รับการป้องกัน = null;
-
* วิธีค่า
-
ตัวจัดการสาธารณะ getSuccessor () {
ผู้สืบทอดกลับมา;
-
-
* ตั้งค่าวัตถุถัดไปเพื่อจัดการคำขอ
-
โมฆะสาธารณะ setSuccessor (ผู้สืบทอดตัวจัดการ) {
this.successor = ผู้สืบทอด;
-
-
* ดำเนินการสมัครเพื่อค่าใช้จ่ายงานเลี้ยง
* ผู้สมัครผู้ใช้ @param
* @param fee จำนวนเงินที่สมัคร
* @return การแจ้งเตือนเฉพาะความสำเร็จหรือความล้มเหลว
-
สตริงนามธรรมสาธารณะ handleFeeRequest (ผู้ใช้สตริงค่าธรรมเนียมสองเท่า);
-
บทบาทตัวประมวลผลเฉพาะ
คัดลอกรหัสรหัสดังต่อไปนี้:
ProjectManager ระดับสาธารณะขยายตัวจัดการ {
@แทนที่
สตริงสาธารณะ handleFeeRequest (ผู้ใช้สตริง ค่าธรรมเนียมสองเท่า) {
สตริง str = "";
//อำนาจของผู้จัดการโครงการค่อนข้างน้อยและสามารถอยู่ภายใน 500 เท่านั้น
ถ้า(ค่าธรรมเนียม < 500)
-
//เพื่อประโยชน์ในการทดสอบ โปรดทำให้มันเรียบง่ายและยอมรับเฉพาะคำขอของ Zhang San เท่านั้น
if("Zhang San".เท่ากับ(ผู้ใช้))
-
str = "ความสำเร็จ: ผู้จัดการโครงการตกลงค่าธรรมเนียมงานเลี้ยงอาหารค่ำของ [" + ผู้ใช้ + "] จำนวนคือ " + ค่าธรรมเนียม + "หยวน";
}อื่น
-
//ไม่มีใครเห็นด้วยเลย
str = "ความล้มเหลว: ผู้จัดการโครงการไม่เห็นด้วยกับค่าธรรมเนียมงานเลี้ยงอาหารค่ำของ [" + ผู้ใช้ + "] จำนวนเงินคือ " + ค่าธรรมเนียม + "หยวน";
-
}อื่น
-
//หากเกิน 500 ให้ส่งต่อให้คนระดับสูงดำเนินการต่อไป
ถ้า(getSuccessor() != null)
-
return getSuccessor().handleFeeRequest(ผู้ใช้, ค่าธรรมเนียม);
-
-
กลับ STR;
-
-
คัดลอกรหัสรหัสดังต่อไปนี้:
DeptManager คลาสสาธารณะขยายตัวจัดการ {
@แทนที่
สตริงสาธารณะ handleFeeRequest (ผู้ใช้สตริง ค่าธรรมเนียมสองเท่า) {
สตริง str = "";
//อำนาจของผู้จัดการแผนกต้องอยู่ภายใน 1,000 เท่านั้น
ถ้า(ค่าธรรมเนียม < 1,000)
-
//เพื่อประโยชน์ในการทดสอบ โปรดทำให้มันเรียบง่ายและยอมรับเฉพาะคำขอของ Zhang San เท่านั้น
if("Zhang San".เท่ากับ(ผู้ใช้))
-
str = "ความสำเร็จ: ผู้จัดการแผนกตกลงค่าธรรมเนียมงานเลี้ยงอาหารค่ำของ [" + ผู้ใช้ + "] จำนวนคือ " + ค่าธรรมเนียม + "หยวน";
}อื่น
-
//ไม่มีใครเห็นด้วยเลย
str = "ความล้มเหลว: ผู้จัดการแผนกไม่เห็นด้วยกับค่าธรรมเนียมงานเลี้ยงอาหารค่ำของ [" + ผู้ใช้ + "] จำนวนคือ " + ค่าธรรมเนียม + "หยวน";
-
}อื่น
-
//หากเกิน 1,000 ให้ส่งต่อให้คนระดับสูงดำเนินการต่อไป
ถ้า(getSuccessor() != null)
-
return getSuccessor().handleFeeRequest(ผู้ใช้, ค่าธรรมเนียม);
-
-
กลับ STR;
-
-
คัดลอกรหัสรหัสดังต่อไปนี้:
GeneralManager ระดับสาธารณะขยายตัวจัดการ {
@แทนที่
สตริงสาธารณะ handleFeeRequest (ผู้ใช้สตริง ค่าธรรมเนียมสองเท่า) {
สตริง str = "";
//ผู้จัดการทั่วไปมีอำนาจมาก ตราบใดที่คำขอมาถึง เขาก็จัดการได้
ถ้า(ค่าธรรมเนียม >= 1,000)
-
//เพื่อประโยชน์ในการทดสอบ โปรดทำให้มันเรียบง่ายและยอมรับเฉพาะคำขอของ Zhang San เท่านั้น
if("Zhang San".เท่ากับ(ผู้ใช้))
-
str = "ความสำเร็จ: ผู้จัดการทั่วไปตกลงที่จะจ่ายค่าอาหารค่ำของ [" + ผู้ใช้ + "] จำนวนคือ " + ค่าธรรมเนียม + "หยวน";
}อื่น
-
//ไม่มีใครเห็นด้วยเลย
str = "ความล้มเหลว: ผู้จัดการทั่วไปไม่เห็นด้วยกับค่าธรรมเนียมงานเลี้ยงอาหารค่ำของ [" + ผู้ใช้ + "] จำนวนคือ " + ค่าธรรมเนียม + "หยวน";
-
}อื่น
-
//หากมีออบเจ็กต์การประมวลผลตามมา ให้ส่งต่อต่อไป
ถ้า(getSuccessor() != null)
-
return getSuccessor().handleFeeRequest(ผู้ใช้, ค่าธรรมเนียม);
-
-
กลับ STR;
-
-
คลาสลูกค้า
คัดลอกรหัสรหัสดังต่อไปนี้:
ลูกค้าคลาสสาธารณะ {
โมฆะสาธารณะคงหลัก (สตริง [] args) {
//รวบรวมห่วงโซ่ความรับผิดชอบก่อน
ตัวจัดการ h1 = GeneralManager ใหม่ ();
ตัวจัดการ h2 = DeptManager ใหม่ ();
ตัวจัดการ h3 = ProjectManager ใหม่ ();
h3.setSuccessor(h2);
h2.setSuccessor(h1);
//เริ่มการทดสอบ
สตริง test1 = h3.handleFeeRequest("จางซาน", 300);
System.out.println("test1 = " + test1);
String test2 = h3.handleFeeRequest("李思", 300);
System.out.println("test2 = " + test2);
System.out.println("---------------------------------------");
String test3 = h3.handleFeeRequest("จางซาน", 700);
System.out.println("test3 = " + test3);
String test4 = h3.handleFeeRequest("李思", 700);
System.out.println("test4 = " + test4);
System.out.println("---------------------------------------");
String test5 = h3.handleFeeRequest("จางซาน", 1500);
System.out.println("test5 = " + test5);
String test6 = h3.handleFeeRequest("李思", 1500);
System.out.println("test6 = " + test6);
-
-
ผลการวิ่งมีดังนี้:
โมเดลห่วงโซ่ความรับผิดชอบที่บริสุทธิ์และไม่บริสุทธิ์
โมเดลสายโซ่ความรับผิดชอบที่บริสุทธิ์กำหนดให้วัตถุตัวประมวลผลเฉพาะสามารถเลือกหนึ่งในสองการกระทำเท่านั้น: การกระทำหนึ่งคือการรับผิดชอบ แต่จะส่งต่อความรับผิดชอบไปยังฝ่ายถัดไป ไม่อนุญาตให้ออบเจ็กต์ตัวประมวลผลเฉพาะส่งต่อความรับผิดชอบหลังจากรับความรับผิดชอบบางอย่างแล้ว
ในโมเดลสายโซ่ความรับผิดชอบล้วนๆ คำร้องขอจะต้องได้รับจากออบเจ็กต์ตัวจัดการ ในโมเดลสายโซ่ความรับผิดชอบที่ไม่บริสุทธิ์ ออบเจ็กต์ผู้รับอาจไม่ได้รับคำขอ
ตัวอย่างที่แท้จริงของแบบจำลองห่วงโซ่ความรับผิดชอบที่ไม่บริสุทธิ์นั้นหาได้ยาก และตัวอย่างที่เห็นโดยทั่วไปคือการนำแบบจำลองห่วงโซ่ความรับผิดชอบที่ไม่บริสุทธิ์ไปใช้ บางคนคิดว่าห่วงโซ่ความรับผิดชอบที่ไม่บริสุทธิ์ไม่ใช่แบบจำลองห่วงโซ่ความรับผิดชอบเลย และอาจสมเหตุสมผล แต่ในระบบจริง ห่วงโซ่ความรับผิดชอบที่บริสุทธิ์นั้นหาได้ยาก หากคุณยืนยันว่าสายโซ่ความรับผิดชอบไม่บริสุทธิ์ นั่นไม่ใช่รูปแบบสายโซ่ความรับผิดชอบ ดังนั้นรูปแบบสายโซ่ความรับผิดชอบจะไม่สมเหตุสมผลมากนัก
การประยุกต์ใช้โมเดลห่วงโซ่ความรับผิดชอบใน Tomcat
ดังที่เราทุกคนทราบกันดีว่า Filter ใน Tomcat ใช้โมเดลสายความรับผิดชอบ นอกเหนือจากการกำหนดค่าที่สอดคล้องกันในไฟล์ web.xml แล้ว การสร้างตัวกรองยังต้องใช้อินเทอร์เฟซ javax.servlet.Filter อีกด้วย
คัดลอกรหัสรหัสดังต่อไปนี้:
TestFilter คลาสสาธารณะใช้ตัวกรอง {
โมฆะสาธารณะ doFilter (คำขอ ServletRequest, การตอบสนอง ServletResponse,
สายโซ่ FilterChain) พ่น IOException, ServletException {
chain.doFilter (คำขอ, การตอบสนอง);
-
โมฆะสาธารณะทำลาย () {
-
โมฆะสาธารณะ init (FilterConfig filterConfig) พ่น ServletException {
-
-
ผลลัพธ์ที่เห็นในโหมด DEBUG มีดังนี้:
ในความเป็นจริง ก่อนที่จะดำเนินการคลาส TestFilter คลาสนั้นจะต้องผ่านคลาสภายในจำนวนมากของ Tomcat อย่างไรก็ตาม การตั้งค่าคอนเทนเนอร์ของ Tomcat ยังเป็นโมเดลสายโซ่ของความรับผิดชอบ โปรดใส่ใจกับคลาสที่ล้อมรอบด้วยกล่องสีแดง มีคลาสที่เรียกว่า ApplicationFilterChain อยู่ในตำแหน่งที่ล้อมรอบด้วยกล่องสีเขียว คลาส ApplicationFilterChain มีบทบาทเป็นตัวประมวลผลแบบนามธรรม และบทบาทของตัวประมวลผลเฉพาะจะถูกเล่นโดยตัวกรองแต่ละตัว
คำถามแรกคือ ApplicationFilterChain เก็บตัวกรองทั้งหมดไว้ที่ไหน
คำตอบจะถูกเก็บไว้ในอาร์เรย์ของออบเจ็กต์ ApplicationFilterConfig ในคลาส ApplicationFilterChain
คัดลอกรหัสรหัสดังต่อไปนี้:
-
*ตัวกรอง
-
ตัวกรอง ApplicationFilterConfig [] ส่วนตัว =
ApplicationFilterConfig ใหม่ [0];
ดังนั้นวัตถุ ApplicationFilterConfig คืออะไร?
ApplicationFilterConfig เป็นคอนเทนเนอร์ตัวกรอง ต่อไปนี้เป็นการประกาศของคลาส ApplicationFilterConfig:
คัดลอกรหัสรหัสดังต่อไปนี้:
-
* การใช้งาน <code>javax.servlet.FilterConfig</code> มีประโยชน์ใน
* การจัดการอินสแตนซ์ตัวกรองที่สร้างอินสแตนซ์เมื่อแอปพลิเคชันเว็บ
* เริ่มแรก
-
* @ผู้เขียน Craig R. McClanahan
* @version $Id: ApplicationFilterConfig.java 1201569 14-11-2554 01:36:07Z kkolinko $
-
ApplicationFilterConfig จะสร้างอินสแตนซ์โดยอัตโนมัติเมื่อเว็บแอปพลิเคชันเริ่มทำงานเป็นครั้งแรก โดยจะอ่านข้อมูลตัวกรองที่กำหนดค่าจากไฟล์ web.xml ของเว็บแอปพลิเคชัน จากนั้นจึงโหลดลงในคอนเทนเนอร์
ฉันเพิ่งเห็นว่าความยาวของอาร์เรย์ ApplicationFilterConfig ที่สร้างขึ้นในคลาส ApplicationFilterChain เป็นศูนย์ มันถูกมอบหมายใหม่เมื่อใด
คัดลอกรหัสรหัสดังต่อไปนี้:
ตัวกรอง ApplicationFilterConfig [] ส่วนตัว =
ApplicationFilterConfig ใหม่ [0];
เกิดขึ้นเมื่อเรียกใช้เมธอด addFilter() ของคลาส ApplicationFilterChain
คัดลอกรหัสรหัสดังต่อไปนี้:
-
* int ที่ให้จำนวนตัวกรองปัจจุบันในห่วงโซ่
-
ส่วนตัว int n = 0;
int สุดท้ายคงที่สาธารณะเพิ่มขึ้น = 10;
คัดลอกรหัสรหัสดังต่อไปนี้:
เป็นโมฆะ addFilter (ApplicationFilterConfig filterConfig) {
// ป้องกันไม่ให้มีการเพิ่มตัวกรองเดียวกันหลายครั้ง
สำหรับ (ตัวกรอง ApplicationFilterConfig: ตัวกรอง)
ถ้า(ตัวกรอง==filterConfig)
กลับ;
ถ้า (n == ตัวกรองความยาว) {
ApplicationFilterConfig[] ตัวกรองใหม่ =
ApplicationFilterConfig ใหม่ [n + เพิ่มขึ้น];
System.arraycopy (ตัวกรอง, 0, ตัวกรองใหม่, 0, n);
ตัวกรอง = ตัวกรองใหม่;
-
ตัวกรอง [n ++] = ตัวกรอง Config;
-
ตัวแปร n ใช้เพื่อบันทึกจำนวนตัวกรองในห่วงโซ่ตัวกรองปัจจุบัน โดยค่าเริ่มต้น n จะเท่ากับ 0 และความยาวของอาร์เรย์ออบเจ็กต์ ApplicationFilterConfig ก็เท่ากับ 0 เช่นกัน ดังนั้นเมื่อมีการเรียกใช้เมธอด addFilter() ในครั้งแรก ถ้า (n == ตัวกรอง .length) เป็นจริง และความยาวของอาร์เรย์ ApplicationFilterConfig มีการเปลี่ยนแปลง จากนั้น filter[n++] = filterConfig; ใส่ตัวแปร filterConfig ลงในอาร์เรย์ ApplicationFilterConfig และเพิ่ม 1 ให้กับจำนวนตัวกรองในห่วงโซ่ตัวกรองปัจจุบัน
แล้วเมธอด addFilter() ของ ApplicationFilterChain เรียกว่าที่ไหน?
อยู่ในเมธอด createFilterChain() ของคลาส ApplicationFilterFactory
คัดลอกรหัสรหัสดังต่อไปนี้:
ApplicationFilterChain สาธารณะ createFilterChain
(คำขอ ServletRequest, Wrapper wrapper, Servlet servlet) {
// รับประเภทผู้มอบหมายงาน
ผู้มอบหมายงานประเภทผู้จัดส่ง = null;
ถ้า (request.getAttribute(DISPATCHER_TYPE_ATTR) != null) {
ผู้มอบหมายงาน = (DispatcherType) request.getAttribute (DISPATCHER_TYPE_ATTR);
-
สตริง requestPath = null;
คุณลักษณะของวัตถุ = request.getAttribute (DISPATCHER_REQUEST_PATH_ATTR);
ถ้า (แอตทริบิวต์ != null){
requestPath = คุณลักษณะ.toString();
-
// หากไม่มีเซิร์ฟเล็ตให้ดำเนินการ ให้คืนค่า null
ถ้า (เซิร์ฟเล็ต == null)
กลับ (โมฆะ);
ดาวหางบูลีน = เท็จ;
// สร้างและเริ่มต้นวัตถุลูกโซ่ตัวกรอง
ApplicationFilterChain filterChain = null;
ถ้า (ร้องขออินสแตนซ์ของคำขอ) {
คำขอ req = (คำขอ) คำขอ;
ดาวหาง = req.isComet();
ถ้า (Globals.IS_SECURITY_ENABLED) {
// ความปลอดภัย: ห้ามรีไซเคิล
filterChain = ApplicationFilterChain ใหม่ ();
ถ้า (ดาวหาง) {
req.setFilterChain(filterChain);
-
} อื่น {
filterChain = (ApplicationFilterChain) req.getFilterChain();
ถ้า (filterChain == null) {
filterChain = ApplicationFilterChain ใหม่ ();
req.setFilterChain(filterChain);
-
-
} อื่น {
//ร้องขอดิสแพตเชอร์ที่ใช้งานอยู่
filterChain = ApplicationFilterChain ใหม่ ();
-
filterChain.setServlet (เซิร์ฟเล็ต);
filterChain.setSupport
(((StandardWrapper)กระดาษห่อ).getInstanceSupport());
// รับการแมปตัวกรองสำหรับบริบทนี้
บริบท StandardContext = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// หากไม่มีการจับคู่ตัวกรอง แสดงว่าเราเสร็จแล้ว
ถ้า ((filterMaps == null) || (filterMaps.length == 0))
กลับ (filterChain);
// รับข้อมูลที่เราต้องการเพื่อจับคู่การแมปตัวกรอง
สตริง servletName = wrapper.getName();
// เพิ่มตัวกรองที่แมปพาธที่เกี่ยวข้องลงในกลุ่มตัวกรองนี้
สำหรับ (int i = 0; i < filterMaps.length; i++) {
ถ้า (!matchDispatcher(filterMaps[i] ,dispatcher)) {
ดำเนินการต่อ;
-
ถ้า (!matchFiltersURL(filterMaps[i], requestPath))
ดำเนินการต่อ;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
ถ้า (filterConfig == null) {
// FIXME - ปัญหาการกำหนดค่าบันทึก
ดำเนินการต่อ;
-
บูลีน isCometFilter = false;
ถ้า (ดาวหาง) {
พยายาม {
isCometFilter = filterConfig.getFilter() อินสแตนซ์ของ CometFilter;
} จับ (ข้อยกเว้นจ) {
// หมายเหตุ: try catch อยู่ที่นั่นเพราะ getFilter มีหลายอย่าง
// ประกาศข้อยกเว้น อย่างไรก็ตาม ตัวกรองได้รับการจัดสรรมาก
// ก่อนหน้านี้
Throwable t = ExceptionUtils.unwrapInvocationTargetException (e);
ExceptionUtils.handleThrowable(t);
-
ถ้า (isCometFilter) {
filterChain.addFilter(filterConfig);
-
} อื่น {
filterChain.addFilter(filterConfig);
-
-
// เพิ่มตัวกรองที่ตรงกับชื่อเซิร์ฟเล็ตที่สอง
สำหรับ (int i = 0; i < filterMaps.length; i++) {
ถ้า (!matchDispatcher(filterMaps[i] ,dispatcher)) {
ดำเนินการต่อ;
-
ถ้า (!matchFiltersServlet(filterMaps[i], servletName))
ดำเนินการต่อ;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
ถ้า (filterConfig == null) {
// FIXME - ปัญหาการกำหนดค่าบันทึก
ดำเนินการต่อ;
-
บูลีน isCometFilter = false;
ถ้า (ดาวหาง) {
พยายาม {
isCometFilter = filterConfig.getFilter() อินสแตนซ์ของ CometFilter;
} จับ (ข้อยกเว้นจ) {
// หมายเหตุ: try catch อยู่ที่นั่นเพราะ getFilter มีหลายอย่าง
// ประกาศข้อยกเว้น อย่างไรก็ตาม ตัวกรองได้รับการจัดสรรมาก
// ก่อนหน้านี้
-
ถ้า (isCometFilter) {
filterChain.addFilter(filterConfig);
-
} อื่น {
filterChain.addFilter(filterConfig);
-
-
// คืนห่วงโซ่ตัวกรองที่เสร็จสมบูรณ์
กลับ (filterChain);
-
โค้ดด้านบนสามารถแบ่งออกเป็นสองส่วน ส่วนแรกก่อนบรรทัด 51 และส่วนที่สองหลังบรรทัด 51
วัตถุประสงค์หลักของย่อหน้าแรกคือการสร้างออบเจ็กต์ ApplicationFilterChain และตั้งค่าพารามิเตอร์บางอย่าง
วัตถุประสงค์หลักของย่อหน้าที่สองคือการรับข้อมูลตัวกรองทั้งหมดจากบริบท จากนั้นใช้ for loop เพื่อสำรวจและเรียก filterChain.addFilter(filterConfig) ใส่ filterConfig ลงในอาร์เรย์ ApplicationFilterConfig ของอ็อบเจ็กต์ ApplicationFilterChain
แล้วเมธอด createFilterChain() ของคลาส ApplicationFilterFactory เรียกว่าที่ไหน?
มันถูกเรียกในเมธอด invoke() ของคลาส StandardWrapperValue
เนื่องจากเมธอด involve() มีความยาว จึงละเว้นหลายตำแหน่ง
คัดลอกรหัสรหัสดังต่อไปนี้:
การร้องขอโมฆะครั้งสุดท้ายสาธารณะ (คำขอคำขอ, การตอบกลับการตอบสนอง)
พ่น IOException, ServletException {
...ละเว้นรหัสกลาง // สร้างสายโซ่ตัวกรองสำหรับคำขอนี้
โรงงาน ApplicationFilterFactory =
ApplicationFilterFactory.getInstance();
ApplicationFilterChain filterChain =
โรงงาน. createFilterChain (คำขอ, กระดาษห่อ, เซิร์ฟเล็ต);
...ละเว้นโค้ดระดับกลาง
filterChain.doFilter(request.getRequest(), response.getResponse());
...ละเว้นโค้ดระดับกลาง
-
กระบวนการปกติควรเป็นดังนี้:
เรียกใช้เมธอด createFilterChain() ของคลาส ApplicationFilterChai ในเมธอด inurge() ของคลาส StandardWrapperValue ---> เรียกใช้เมธอด addFilter() ของคลาส ApplicationFilterChain ในเมธอด createFilterChain() ของคลาส ApplicationFilterChai ---> เรียกใช้เมธอด วิธีการ addFilter() ของคลาส ApplicationFilterChain กำหนดค่าให้กับอาร์เรย์ ApplicationFilterConfig
ตามโค้ดข้างต้น จะเห็นได้ว่าเมธอด inurge() ของคลาส StandardWrapperValue จะยังคงดำเนินการเมธอด doFilter() ของคลาส ApplicationFilterChain หลังจากดำเนินการเมธอด createFilterChain() แล้ว จากนั้นเมธอด InternalDoFilter() จะเป็น เรียกในเมธอด doFilter()
ต่อไปนี้เป็นส่วนหนึ่งของรหัสของวิธีการinternalDoFilter()
คัดลอกรหัสรหัสดังต่อไปนี้:
// เรียกตัวกรองถัดไปหากมี
ถ้า (pos < n) {
//รับตัวกรองถัดไปแล้วเลื่อนตัวชี้ลงหนึ่งตำแหน่ง
//pos ระบุว่าตัวกรองใดที่ ApplicationFilterChain ปัจจุบัน (สายโซ่ตัวกรองปัจจุบัน) ดำเนินการ
ApplicationFilterConfig filterConfig = ตัวกรอง [pos++];
ตัวกรองตัวกรอง = null;
พยายาม {
// รับอินสแตนซ์ของตัวกรองที่ชี้อยู่ในปัจจุบัน
ตัวกรอง = filterConfig.getFilter();
support.fireInstanceEvent (InstanceEvent.BEFORE_FILTER_EVENT,
กรอง, ร้องขอ, ตอบกลับ);
ถ้า (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute (Globals.ASYNC_SUPPORTED_ATTR,
บูลีน.FALSE);
-
ถ้า (Globals.IS_SECURITY_ENABLED) {
คำขอ ServletRequest สุดท้าย = คำขอ;
สุดท้าย ServletResponse res = การตอบสนอง;
เงินต้น เงินต้น =
((HttpServletRequest) ร้องขอ).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege
("doFilter", ตัวกรอง, classType, args, เงินต้น);
} อื่น {
//เรียกเมธอด doFilter() ของ Filter
filter.doFilter(คำขอ, การตอบกลับ, สิ่งนี้);
-
filter.doFilter(request, response, this); นี่คือการเรียกเมธอด doFilter() ใน TestFilter ที่เราสร้างไว้ก่อนหน้านี้ เมธอด doFilter() ใน TestFilter จะยังคงเรียกเมธอด chain.doFilter(request, response); และจริงๆ แล้ว chain นี้ก็คือ ApplicationFilterChain ดังนั้นกระบวนการเรียกจึงกลับไปเรียก dofilter และเรียกเมธอด InternalDoFilter ด้านบน และสิ่งนี้ การดำเนินการจะดำเนินต่อไปจนกระทั่งตัวกรองอยู่ภายในดำเนินการทั้งหมด
หากมีการกำหนดตัวกรองสองตัว ผลลัพธ์ของการดีบักจะเป็นดังนี้: