Java Spring 5 FATURE FANDICAL WEBWORK FRAMEWORK
ยกตัวอย่าง
เริ่มต้นด้วยข้อความที่ตัดตอนมาจากแอปพลิเคชันตัวอย่าง ด้านล่างเป็นไลบรารีข้อมูลการตอบกลับที่เปิดเผยวัตถุบุคคล คล้ายกับห้องสมุดข้อมูลแบบดั้งเดิมที่ไม่ตอบสนองยกเว้นว่าจะส่งคืนฟลักซ์ <person> และรายการผลตอบแทนแบบดั้งเดิม <person> และสถานที่ที่โมโน <person> ส่งคืนบุคคล Mono <Void> ใช้เป็นธงที่สมบูรณ์: ระบุว่าการบันทึกเสร็จสมบูรณ์เมื่อใด
ส่วนต่อประสานสาธารณะบุคคล {mono <person> getPerson (ID int); ฟลักซ์ <person> allpeople (); Mono <Void> SavePerson (Mono <Person> บุคคล);}นี่คือวิธีที่เราเปิดเผยไลบรารีด้วยเฟรมเวิร์กการทำงานใหม่:
RouterFunction <?> route = เส้นทาง (get ("/person/{id}"), คำขอ -> {mono <person> person = mono.justorempty (request.pathvariable ("id")) .map (จำนวนเต็ม: .and (เส้นทาง (รับ ("/บุคคล"), คำขอ -> {ฟลักซ์ <person> ผู้คน = ที่เก็บ. allpeople (); return response.ok (). ร่างกาย (FromPublisher (คน, บุคคล. class));}). และเส้นทาง (โพสต์ ("/บุคคล") Response.ok (). build (repository.saveperson (บุคคล));}));ที่นี่เราจะแนะนำวิธีการทำงานเช่นในเครื่องปฏิกรณ์ Netty:
httphandler httphandler = routerfunctions.tohttphandler (เส้นทาง); reactorhttphandleradapter อะแดปเตอร์ = ใหม่ reactorhtttphandleradapter (httphandler); httpserver เซิร์ฟเวอร์ = httpserver.create
สิ่งสุดท้ายที่ต้องทำคือลอง:
$ curl 'http: // localhost: 8080/person/1' {"name": "John doe", "อายุ": 42}มีการแนะนำเพิ่มเติมด้านล่างให้ขุดลึกลงไป!
ส่วนประกอบหลัก
ฉันจะแนะนำเฟรมเวิร์กโดยการอธิบายองค์ประกอบหลักอย่างละเอียด: HandlerFunction, RouterFunction และ FilterFunction อินเทอร์เฟซทั้งสามนี้รวมถึงประเภทอื่น ๆ ทั้งหมดที่อธิบายไว้ในบทความสามารถพบได้ใน org.springframework.web.reactive.function แพ็คเกจ
HandlerFunction
จุดเริ่มต้นของเฟรมเวิร์กใหม่นี้คือ HandlerFunction <T> ซึ่งโดยทั่วไปแล้วฟังก์ชั่น <การร้องขอการตอบสนอง <t>> ซึ่งมีการกำหนดคำขอและการตอบกลับใหม่และอินเทอร์เฟซที่ไม่เปลี่ยนแปลงนั้นเป็นมิตรเพื่อให้ JDK-8 DSL ไปยังข้อความ HTTP มันเป็นเครื่องมือสร้างที่สะดวกสำหรับหน่วยงานตอบสนองอาคารคล้ายกับสิ่งที่คุณเห็นใน ResponseEntity สอดคล้องกับคำอธิบายประกอบ HandlerFunction เป็นวิธีที่มี @requestmapping
นี่คือตัวอย่างง่ายๆของฟังก์ชั่นการประมวลผล "Hello World" โดยส่งคืนข้อความตอบกลับด้วย 200 รัฐและเนื้อหาของสตริง:
HandlerFunction <String> helloWorld = คำขอ -> response.ok (). body (fromObject ("Hello World"));ดังที่เราเห็นในตัวอย่างข้างต้นฟังก์ชั่นการจัดการนั้นตอบสนองได้อย่างสมบูรณ์โดยการสร้างเครื่องปฏิกรณ์: พวกเขายอมรับฟลักซ์, โมโนหรือผู้เผยแพร่สตรีมอื่น ๆ ที่เกี่ยวข้องเป็นประเภทการตอบสนอง
สิ่งหนึ่งที่ควรทราบคือ HandlerFunction นั้นไม่มีผลข้างเคียงเพราะมันส่งคืนการตอบกลับแทนที่จะถือว่าเป็นพารามิเตอร์ (ดู Servlet.Service (ServletRequest, ServletResponse) ไม่มีประโยชน์มากมายที่ไม่มีผลข้างเคียง: ง่ายต่อการทดสอบเขียนและปรับให้เหมาะสม
Routerfunction
คำขอที่เข้ามาจะถูกส่งไปยังฟังก์ชั่น Handler ด้วย RouterFunction <T> (เช่นฟังก์ชั่น <การร้องขอ, ตัวเลือก <handlerFunction <t>>) และถูกส่งไปยังตัวจัดการหากตรงกับ มิฉะนั้นผลลัพธ์ที่ว่างเปล่าจะถูกส่งคืน วิธีการกำหนดเส้นทางทำงานคล้ายกับคำอธิบายประกอบ @requestmapping อย่างไรก็ตามมีความแตกต่างอย่างมีนัยสำคัญอีกประการหนึ่ง: เมื่อใช้คำอธิบายประกอบเส้นทางจะถูก จำกัด อยู่ที่ช่วงที่ค่าคำอธิบายประกอบสามารถแสดงออกได้และเป็นการยากที่จะจัดการกับการซ้อนทับของวิธีการเหล่านี้ เมื่อใช้วิธีการกำหนดเส้นทางรหัสจะอยู่ที่นั่นและสามารถเขียนทับหรือแทนที่ได้อย่างง่ายดาย
ด้านล่างเป็นตัวอย่างของฟังก์ชั่นการกำหนดเส้นทางที่มีฟังก์ชั่นการประมวลผลแบบฝัง มันดูยาวไปหน่อย แต่ไม่ต้องกังวล: เราจะหาวิธีที่จะทำให้สั้นลง
RouterFunction <String> HelloWorLdRoute = คำขอ -> {ถ้า (request.path (). เท่ากับ ("/hello -world"))) {return potainal.of (r -> response.ok (). ร่างกาย ("" Hello World "))); } else {return potlical.empty (); -โดยทั่วไปไม่จำเป็นต้องเขียนวิธีการกำหนดเส้นทางที่สมบูรณ์ แต่แทนที่จะแนะนำ RouterFunctions.route () แบบคงที่เพื่อให้คุณสามารถสร้างวิธีการกำหนดเส้นทางโดยใช้สูตรการตัดสินคำขอ (คำขอ PREDICATE) (เช่นเพรดิเคต <Equest>) และ HandlerFunction) หากการตัดสินสำเร็จวิธีการประมวลผลจะถูกส่งคืนมิฉะนั้นผลลัพธ์ที่ว่างเปล่าจะถูกส่งคืน ต่อไปนี้เป็นตัวอย่างด้านบนโดยใช้วิธีการเส้นทาง:
RouterFunction <String> helloWorldRoute = routerfunctions.route (คำขอ -> request.path (). เท่ากับ ("/hello -world"), คำขอ -> response.ok (). body ("Hello World")));คุณสามารถ (แบบคงที่) นำเข้า requestpredicates* เพื่อเข้าถึงภาคการศึกษาที่ใช้กันทั่วไปตามเส้นทางวิธี HTTP ประเภทเนื้อหา ฯลฯ ด้วยมันเราสามารถทำให้ HelloWorldRoute ง่ายขึ้น:
RouterFunction <String> helloWorldRoute = routerfunctions.route (requestpredicates.path ("/hello -world"), คำขอ -> response.ok (). ร่างกาย ("Hello World")));ฟังก์ชั่นการรวมกัน
ฟังก์ชั่นการกำหนดเส้นทางสองฟังก์ชั่นสามารถสร้างฟังก์ชั่นการกำหนดเส้นทางใหม่เส้นทางไปยังฟังก์ชั่นการประมวลผลทั้งสอง: หากฟังก์ชั่นแรกไม่ตรงกัน คุณสามารถรวมฟังก์ชั่นการกำหนดเส้นทางสองแบบเช่นนี้ได้โดยการโทรหา RouterFunction.and ():
RouterFunction <?> เส้นทาง = เส้นทาง (เส้นทาง ("/hello -world"), คำขอ -> response.ok (). ร่างกาย (จากวัตถุ ("Hello World"))))) และ (เส้นทาง (เส้นทาง ("/คำตอบ"), คำขอ -> response.ok ().หากเส้นทางที่ตรงกับ /Hello-World ข้างต้นจะตอบสนองต่อ "Hello World" และถ้า /ตรงข้ามตรงกับมันจะกลับ "42" ในเวลาเดียวกัน หากไม่ตรงกันจะส่งคืนตัวเลือกที่ว่างเปล่า โปรดทราบว่าฟังก์ชั่นการกำหนดเส้นทางแบบรวมจะถูกดำเนินการตามลำดับดังนั้นจึงเหมาะสมที่จะใส่ฟังก์ชั่นทั่วไปก่อนฟังก์ชั่นเฉพาะ
นอกจากนี้คุณยังสามารถรวมการร้องขอเพรดิเคตโดยการโทรและหรือหรือ สิ่งนี้ใช้งานได้เช่นนี้: สำหรับและหากมีการจับคู่สองรายการที่ตรงกับผลการศึกษาผลการแข่งขันและถ้าหนึ่งในสองการแข่งขันแล้วจับคู่ ตัวอย่างเช่น:
RouterFunction <?> Route = เส้นทาง (วิธีการ (httpmethod.get) .and (path ("/hello -world")), คำขอ -> response.ok (). ร่างกาย ("Hello World")))). และเส้นทาง (วิธีการ (httpmethod.get) Response.ok (). ร่างกาย (FromObject ("42"))));ในความเป็นจริงภาคการประชุมส่วนใหญ่ที่พบในคำขอ predicates จะรวมกัน! ตัวอย่างเช่น requestpredicates.get (String) เป็นองค์ประกอบของ requestpredicates.method (httpmethod) และ requestpredicates.path (String) ดังนั้นเราสามารถเขียนรหัสข้างต้นใหม่เป็น:
RouterFunction <?> route = เส้นทาง (รับ ("/hello -world"), คำขอ -> response.ok (). body ("hello world"))). และ (เส้นทาง ("("//คำตอบ "), คำขอ -> response.ok ().วิธีการอ้างอิง
โดยวิธีการ: จนถึงตอนนี้เราได้เขียนฟังก์ชั่นการประมวลผลทั้งหมดเป็นนิพจน์อินไลน์แลมบ์ดา ในขณะที่สิ่งนี้ทำงานได้ดีในการสาธิตและตัวอย่างสั้น ๆ แต่ก็ต้องมีการกล่าวว่ามีแนวโน้มที่จะก่อให้เกิด "ความสับสน" เพราะคุณต้องการผสมข้อกังวลสองประการ: ขอการกำหนดเส้นทางและการประมวลผลการร้องขอ ดังนั้นเราต้องการดูว่ามันสามารถทำให้สิ่งต่าง ๆ ง่ายขึ้นหรือไม่ ก่อนอื่นเราสร้างคลาสที่มีรหัสการประมวลผล:
DemoHandler คลาส {การตอบสนองสาธารณะ <String> HelloWorld (คำขอคำขอ) {return response.ok (). ร่างกาย (จากวัตถุ ("Hello World")); }/ * http://www.manongjc.com/article/1590.html */การตอบสนองต่อสาธารณะ <String> theanswer (คำขอคำขอ) {return response.ok (). ร่างกาย (FromObject ("42")); -โปรดทราบว่าทั้งสองวิธีมีธงที่เข้ากันได้กับฟังก์ชั่นการประมวลผล สิ่งนี้ช่วยให้เราสามารถใช้การอ้างอิงวิธีการ:
DemoHandler Handler = New DemoHandler (); // หรือได้รับผ่าน dirouterfunction <?> เส้นทาง = เส้นทาง (รับ ("/hello-world"), handler :: helloWorld). และ (เส้นทาง (รับ ("/คำตอบ"), Handler :: theanswer));ตัวกรอง
เส้นทางที่แมปโดยฟังก์ชั่นการกำหนดเส้นทางสามารถกรองได้โดยการโทรหา RouterFunction.filter (FilterFunction <T, R>), ที่ FilterFunction <t, r> เป็น bifunction <คำขอ, handlerFunction <t>, การตอบสนอง <r>> พารามิเตอร์ตัวจัดการของฟังก์ชั่นแสดงถึงรายการถัดไปในห่วงโซ่ทั้งหมด: นี่คือ HandlerFunction ทั่วไป แต่ถ้ามีตัวกรองหลายตัวติดอยู่ก็สามารถเป็นตัวกรองอื่นได้ มาเพิ่มตัวกรองบันทึกไปยังเส้นทาง:
// http: //www.manongjc.comrouterfunction <?> route = เส้นทาง (รับ ("/hello-world"), handler :: helloWorld). และเส้นทาง (get ("/คำตอบ"), handler :: theanswer)) การตอบสนอง <?> การตอบสนอง = next.handle (คำขอ);ควรสังเกตว่าจะเรียกตัวจัดการถัดไปเป็นตัวเลือกหรือไม่ สิ่งนี้มีประโยชน์มากในรูปแบบความปลอดภัยและการแคช (เช่นการโทรต่อไปเฉพาะเมื่อผู้ใช้มีสิทธิ์เพียงพอ)
เนื่องจากเส้นทางเป็นฟังก์ชั่นการกำหนดเส้นทางที่ไม่มีที่สิ้นสุดเราจึงรู้ว่าข้อมูลการตอบสนองประเภทใดที่ตัวจัดการถัดไปจะกลับมา นี่คือเหตุผลที่เราลงเอยด้วยการตอบสนอง <?> ในตัวกรองของเราและตอบสนองต่อร่างกายด้วยวัตถุ ในคลาส Handler ทั้งสองวิธีส่งกลับการตอบสนอง <String> ดังนั้นจึงควรมีตัวตอบสนองสตริง เราสามารถทำได้โดยใช้ RouterFunction.andsame () แทนและ () วิธีการรวมกันนี้ต้องการให้ฟังก์ชันการกำหนดเส้นทางพารามิเตอร์เป็นประเภทเดียวกัน ตัวอย่างเช่นเราสามารถทำการตอบกลับทั้งหมด:
RouterFunction <String> เส้นทาง = เส้นทาง (รับ ("/hello-world"), handler :: helloWorld) .andsame (เส้นทาง (รับ ("/คำตอบ"), handler :: theanswer)) Response.from (การตอบสนอง). Body (FromObject (newBody));การใช้คำอธิบายประกอบสามารถใช้ฟังก์ชั่นที่คล้ายกันได้โดยใช้ @ControllerAdvice และ/หรือ ServletFilter
เรียกใช้เซิร์ฟเวอร์
ทั้งหมดนี้เป็นเรื่องปกติ แต่สิ่งหนึ่งที่ฉันลืม: เราจะเรียกใช้ฟังก์ชั่นเหล่านี้ในเซิร์ฟเวอร์ HTTP จริงได้อย่างไร คำตอบนั้นไม่ต้องสงสัยเลยว่าโทรหาฟังก์ชั่นอื่น คุณสามารถแปลงฟังก์ชั่นการกำหนดเส้นทางเป็น httphandler โดยใช้ RouterFunctions.tohttPhandler () Httphandler เป็นสิ่งที่เป็นนามธรรมที่แนะนำให้รู้จักกับ Spring 5.0 M1: ช่วยให้คุณทำงานในการตอบสนองที่หลากหลาย: เครื่องปฏิกรณ์ Netty, RxNetty, Servlet 3.1+ และ Undertow ในตัวอย่างนี้เราได้แสดงให้เห็นว่ามันเป็นเหมือนการวิ่งเส้นทางในเครื่องปฏิกรณ์ Netty สำหรับ Tomcat ดูเหมือนว่า:
httphandler httphandler = routerfunctions.tohttphandler (เส้นทาง); httpservlet servlet = ใหม่ servlethttphandleradapter (httphandler); tomcat server = new tomcat (); บริบท rootcontext = server.addContext ("" System.getProperty ("java.io.tmpdir")); tomcat.addservlet (rootcontext, "servlet", servlet); rootcontext.addservletmapping ("/", "servlet"); tomcatserver.start ();สิ่งหนึ่งที่ควรทราบคือรหัสข้างต้นไม่ได้ขึ้นอยู่กับบริบทของแอปพลิเคชันฤดูใบไม้ผลิ เช่น JDBCTEMPLATE และคลาสยูทิลิตี้สปริงอื่น ๆ การใช้บริบทแอปพลิเคชันเป็นทางเลือก: คุณสามารถเชื่อมต่อฟังก์ชั่นตัวจัดการและการกำหนดเส้นทางในบริบท แต่ไม่จำเป็น
นอกจากนี้โปรดทราบว่าคุณสามารถแปลงฟังก์ชั่นการกำหนดเส้นทางเป็น handlermapping เพื่อให้สามารถทำงานใน dispatcherHandler (อาจต้องใช้ @controllers ตอบสนอง)
สรุปแล้ว
ให้ฉันวาดข้อสรุปผ่านบทสรุปสั้น ๆ :
เพื่อให้คุณมีความเข้าใจที่ครอบคลุมมากขึ้นฉันได้สร้างโครงการตัวอย่างง่ายๆโดยใช้เฟรมเวิร์กเว็บที่ใช้งานได้ ดาวน์โหลดที่อยู่
ขอบคุณสำหรับการอ่านฉันหวังว่ามันจะช่วยคุณได้ ขอบคุณสำหรับการสนับสนุนเว็บไซต์นี้!