Java Spring 5 ميزة جديدة إطار عمل ويب
إعطاء مثال
لنبدأ ببعض مقتطفات من تطبيق العينة. فيما يلي مكتبة معلومات استجابة تعرض كائنات الشخص. يشبه إلى حد كبير مكتبة المعلومات التقليدية غير المستجيبة ، باستثناء أنها تُرجع Flux <Person> وقائمة الإرجاع التقليدية <Person> والمكان الذي يعيد فيه Mono <Phone> الشخص. يتم استخدام Mono <Void> كعلم إكمال: يشير إلى متى يتم الانتهاء من الحفظ.
الواجهة العامة personRepository {mono <profer> getPerson (int id) ؛ Flux <Person> allpeople () ؛ Mono <Void> SavePerson (Mono <Phone> الشخص) ؛}إليك كيفية تعريض مكتبة مع إطار عمل الويب الوظيفي الجديد:
RouterFunction <؟> route = Route (get ("/person/{id}") ، request -> {mono <profer> person = mono.justorempty (request.pathvariable ("id") . و (Route (get ("/person") ، request -> {flux <perienc> people = ropository.allpeople () ؛ return response.ok (). الجسم (frompublisher (people ، person.class)) ؛})). Response.ok (). build (ropository.saveperson (person)) ؛})) ؛سنقدم هنا كيفية التشغيل ، على سبيل المثال في مفاعل Netty:
httphandler httphandler = routerfunctions.thttphandler (Route) ؛ ReactorHttphandlerAdapter Adapter = ReactorHttphandlerAdapter (httphandler) ؛ httpserver server = httpserver.create.
آخر شيء فعله هو تجربتها:
$ curl 'http: // localhost: 8080/person/1' {"name": "John Doe" ، "Age": 42}هناك المزيد من المقدمات أدناه ، دعنا نحفر بشكل أعمق!
المكونات الأساسية
سأقدم الإطار من خلال شرح المكونات الأساسية بدقة: HandlerFunction و RouterFunction و FilterFunction. يمكن العثور على هذه الواجهات الثلاثة ، وكذلك جميع الأنواع الأخرى الموضحة في المقالة ، في حزمة org.springframework.web.reactive.function.
معالج
نقطة الانطلاق لهذا الإطار الجديد هي معالجتها <T> ، والتي هي في الأساس وظيفة <الطلب ، والاستجابة <T >> ، حيث يتم تحديد الطلب والاستجابة حديثًا ، والواجهة غير المتغيرة ودية لتوفير JDK-8 DSL لرسائل HTTP الأساسية. إنها أداة بناء مريحة لبناء كيانات الاستجابة ، تشبه إلى حد بعيد ما تراه في الاستجابة. المقابل لشرح معالج الوظيفي هو طريقة مع requestMapping.
فيما يلي مثال بسيط على وظيفة معالجة "Hello World" ، وإعادة رسالة استجابة مع 200 ولاية ومجموعة من السلسلة:
HandlerFunction <string> helloWorld = request -> response.ok (). الجسم (fromObject ("Hello World")) ؛كما رأينا في المثال أعلاه ، فإن وظائف المناولة تستجيب تمامًا عن طريق البناء على أساس المفاعل: فهي تقبل التدفق أو الأحادي أو أي ناشر دفق آخر مقابل كنوع استجابة.
شيء واحد يجب ملاحظة أن HandlerFunction نفسه ليس له أي آثار جانبية لأنه يعيد الاستجابة بدلاً من معاملته كمعلمة (انظر Servlet.Service (servletrequest ، servletResponse) ، وهو أساسا biconsumer <servletRequest ، servletResponse>). هناك العديد من الفوائد دون أي آثار جانبية: من السهل الاختبار والكتابة والتحسين.
جهاز التوجيه
يتم توجيه الطلب الوارد إلى دالة المعالج باستخدام RouterFunction <T> (أي الوظيفة <الطلب ، اختياري <HandlerFunction <T >>) ويتم توجيهه إلى المعالج إذا كان يتطابق معه ؛ خلاف ذلك ، يتم إرجاع نتيجة فارغة. تعمل طريقة التوجيه مماثلة لشروح requestmapping. ومع ذلك ، هناك فرق كبير آخر: عند استخدام التعليقات التوضيحية ، سيقتصر المسار على النطاق الذي يمكن أن تعبر عنه القيمة المشروحة ، ومن الصعب التعامل مع تراكب هذه الأساليب ؛ عند استخدام طريقة التوجيه ، يكون الكود موجودًا ويمكن استبداله بسهولة أو استبداله.
فيما يلي مثال على وظيفة التوجيه مع وظيفة معالجة مضمنة. تبدو طويلة بعض الشيء ، لكن لا تقلق: سنجد طريقة لجعلها أقصر.
RouterFunction <string> HelloWorLdRoute = request -> {if (request.path (). equals ("/hello -world"))) } else {return eptarical.empty () ؛ }} ؛بشكل عام ، ليست هناك حاجة لكتابة طريقة توجيه كاملة ، ولكن بدلاً من ذلك ، قدمت بشكل ثابت RouterFunctions.Route () ، بحيث يمكنك إنشاء طريقة توجيه باستخدام صيغة حكم الطلب (requestpredicate) (أي أن المسند <squest> و HandlerFunction). إذا نجح الحكم ، فسيتم إرجاع طريقة المعالجة ، وإلا سيتم إرجاع النتيجة الفارغة. ما يلي هو المثال أعلاه باستخدام طريقة المسار:
RouterFunction <string> HelloWorLdRoute = RouterFunctions.Route (request -> request.path
يمكنك (بشكل ثابت) استيراد طلبات الطلب.* للوصول إلى المستحقات شائعة الاستخدام ، استنادًا إلى المسارات ، وطرق HTTP ، وأنواع المحتوى ، وما إلى ذلك ، يمكننا جعل HelloWorldRoute أبسط:
RouterFunction <string> helloWorldRoute = RouterFunctions.Route (requestPredicates.path ("/hello -world") ، request -> reponse.ok ().وظائف الجمع
يمكن أن تشكل وظيفتين للتوجيه وظيفة توجيه جديدة ، توجيهًا إلى أي وظيفة معالجة: إذا كانت الوظيفة الأولى لا تتطابق ، فسيتم تنفيذ الحالة الثانية. يمكنك الجمع بين وظيفتين للتوجيه مثل هذا عن طريق استدعاء RouterFunction. و ():
RouterFunction <؟> Route = Route ("/"/Hello -World ") ، request -> reponse.ok (). الجسم (من OBJect (" Hello World "))))).إذا كان المسار يتطابق مع العالم المرحب ، فسيستجيب ما ورد أعلاه إلى "Hello World" ، وإذا كان /الإجابات ، فسيعود "42" في نفس الوقت. إذا لم تتطابق ، يتم إرجاع اختياري فارغ. لاحظ أن وظائف التوجيه المشتركة يتم تنفيذها بالتسلسل ، لذلك من المنطقي وضع وظائف عامة قبل الوظيفة المحددة.
يمكنك أيضًا الجمع بين المتنبئين عن طريق الاتصال أو أو أو. هذا يعمل على هذا النحو: من أجل ، وإذا كان هناك تطابقان معطى ، فإن النتيجة تتنبأ بمطابقة ، وإذا كانت إحدى المباريات ، ثم أو تطابق. على سبيل المثال:
RouterFunction <؟> Route = Route (method (httpmethod.get). و (path ("/hello -world")) ، request -> reponse.ok (). استجابة. OOK (). الجسم (FromObject ("42"))))) ؛في الواقع ، يتم الجمع بين معظم المتنبئين الموجودين في requestpredicates! على سبيل المثال ، requestPredicates.get (سلسلة) هو تكوين من requestpredicates.method (httpmethod) و requestpredicates.path (سلسلة). لذلك ، يمكننا إعادة كتابة الرمز أعلاه على النحو التالي:
RouterFunction <؟> Route = Route (get ("/hello -world") ، request -> response.ok (). body (fromObject ("Hello World"))).طريقة المرجع
بالمناسبة: حتى الآن كتبنا جميع وظائف المعالجة كتعبيرات Lambda مضمنة. على الرغم من أن هذا يعمل بشكل جيد في الأمثلة التجريبية والقصيرة ، إلا أنه يجب القول أن هناك ميلًا للتسبب في "الارتباك" لأنك تريد مزج مخاوفين: طلب التوجيه وطلب المعالجة. لذلك ، نريد أن نرى ما إذا كان يمكن أن يجعل الأمور أكثر بساطة. أولاً ، نقوم بإنشاء فئة تحتوي على رمز المعالجة:
class demoHandler {public repose <string> HelloWorld (request request) {return response.ok (). body (fromObject ("Hello World")) ؛ }/ * http://www.manongjc.com/article/1590.html */استجابة عامة <string> theanswer (طلب طلب) {return review.ok (). الجسم (من OBject ("42")) ؛ }}لاحظ أن كلتا الطريقتين لها علامة تتوافق مع وظيفة المعالجة. هذا يسمح لنا باستخدام مراجع الطريقة:
معالج demoHandler = جديد demoHandler () ؛ // أو الحصول على عبر dirouterfunction <؟> Route = Route (Get ("/Hello-World") ، Handler :: HelloWorld). و (Route (Get ("/the-Answer") ، Handler :: theanswer)) ؛FilterFunction
يمكن ترشيح المسار الذي تم تعيينه بواسطة وظيفة التوجيه عن طريق استدعاء RouterFunction.Filter (FilterFunction <T ، R>) ، حيث يكون FilterFunction <T ، R> بشكل أساسي ثنائي الوظيفة <طلب ، HandlerFunction <T> ، الاستجابة <r>. تمثل معلمة المعالج للدالة العنصر التالي في السلسلة بأكملها: هذا هو معالج نموذجي ، ولكن إذا تم إرفاق مرشحات متعددة ، فيمكن أن يكون أيضًا مرشحًا آخر. دعنا نضيف مرشح سجل إلى الطريق:
// http: //www.manongjc.comrouterfunction <؟> route = route (get ("/hello-world") ، Handler :: HelloWorld). و (Route (get ("/the-aswer") ، handler :: theanswer)) .filter ((اطلب)- استجابة <؟> next.handle (request) ؛تجدر الإشارة إلى أن ما إذا كان الاتصال بالمعالج التالي هو اختياري. هذا مفيد للغاية في مخططات الأمن والتخزين المؤقت (مثل الاتصال التالي فقط عندما يكون لدى المستخدم أذونات كافية).
نظرًا لأن الطريق هو وظيفة توجيه غير محدودة ، فنحن نعرف نوع معلومات الاستجابة التي سيعودها المعالج التالي. هذا هو السبب في أننا انتهى بنا الأمر بالاستجابة <؟> في مرشحنا والرد على الجسم مع الكائن. في فئة المعالج ، تقوم كلتا الطريقتين بإرجاع استجابة <string> ، لذلك يجب أن يكون من الممكن أن يكون لديك هيئة استجابة للسلسلة. يمكننا القيام بذلك باستخدام RouterFunction.andsame () بدلاً من و (). تتطلب طريقة المركب هذه أن تكون وظيفة توجيه المعلمة من نفس النوع. على سبيل المثال ، يمكننا أن نجعل جميع الردود تستفيد منها:
RouterFunction <string> route = Route (get ("/hello-world") ، Handler :: HelloWorld) .andsame (Route (get ("/the-Asswer") ، Handler :: theanswer)) .filter ((request ، next)-> {response <string> response = next.handle (request) ؛ استجابة.باستخدام التعليقات التوضيحية ، يمكن تنفيذ وظائف مماثلة باستخدام ControllerAdvice و/أو servletFilter.
تشغيل الخادم
كل هذا على ما يرام ، لكن شيء واحد نسيته: كيف يمكننا تشغيل هذه الوظائف في خادم HTTP الفعلي؟ الجواب هو بلا شك من خلال استدعاء وظيفة أخرى. يمكنك تحويل وظيفة التوجيه إلى httphandler باستخدام RouterFunctions.Thttphandler (). Httphandler هو تجريد استجابة تم تقديمه إلى Spring 5.0 M1: يتيح لك الركض في أوقات استجابة مختلفة: مفاعل Netty و Rxnetty و Servlet 3.1+ و Leadtow. في هذا المثال ، أظهرنا كيف يشبه الطريق في مفاعل Netty. ل tomcat ، يبدو هكذا:
httphandler httphandler = routerfunctions.thttphandler (route) ؛ httpservlet servlet = new servlethttphandleradapter (httphandler) ؛ tomcat server = new tomcat () ؛ context rootcontext = server.addcontext ("" ، "،" ، System.getProperty ("java.io.tmpdir")) ؛ tomcat.addservlet (rootContext ، "servlet" ، servlet) ؛ rootContext.AddServletMapping ("/" ، "servlet") ؛ tomcatserver.start () ؛شيء واحد يجب ملاحظته هو أن الكود أعلاه لا يعتمد على سياق تطبيق الربيع. مثل JDBCTEMPLATE وفئات أدوات الربيع الأخرى ، فإن استخدام سياق التطبيق اختياري: يمكنك توصيل وظائف المعالج والتوجيه في السياق ، لكنه غير مطلوب.
لاحظ أيضًا أنه يمكنك أيضًا تحويل وظيفة التوجيه إلى معالجة المعالجة بحيث يمكن تشغيلها في المرسل (قد تتطلب استجابة Controllers).
ختاماً
اسمحوا لي أن أستنتج من خلال ملخص موجز:
لإعطائك فهمًا أكثر شمولاً ، قمت بإنشاء مشروع مثال بسيط باستخدام إطار الويب الوظيفي. تنزيل عنوان
شكرا لك على القراءة ، آمل أن تساعدك. شكرا لك على دعمك لهذا الموقع!