1. مقدمة إلى Servlet
Servlet هي تقنية تقدمها شركة Sun لتطوير موارد الويب الديناميكية.
يوفر Sun واجهة Servlet في واجهة برمجة التطبيقات الخاصة بها. إذا أراد المستخدمون إرسال مورد ويب ديناميكي (أي ، قم بتطوير برنامج Java لإخراج البيانات إلى المتصفح) ، فيجب عليهم إكمال الخطوتين التاليتين:
1. اكتب فئة Java لتنفيذ واجهة Servlet.
2. نشر فئات Java المتقدمة على خادم الويب.
وفقًا لعادة الاسم التقليدية ، فإننا ندعو عادة برامج Java التي تنفذ Servlet Interface Servlet.
2. عملية تشغيل servlet
يتم استدعاء برنامج Servlet بواسطة خادم الويب. بعد أن يتلقى خادم الويب طلب وصول العميل Servlet:
① يقوم خادم الويب أولاً بالتحقق مما إذا كان قد تم تحميله وإنشاء كائن مثيل لـ Servlet. إذا كان الأمر كذلك ، ثم قم بتنفيذ الخطوة 4 مباشرة ؛ خلاف ذلك ، تنفيذ الخطوة 2.
② تحميل وإنشاء كائن مثيل من servlet.
③ Call the init () طريقة كائن مثيل servlet.
④ قم بإنشاء كائن httpservletrequest لتغليف رسائل طلب HTTP وكائن httpservletresponse الذي يمثل رسائل استجابة HTTP ، ثم اتصل بالطريقة Servlet's Service () وتمرير كائنات الطلب والاستجابة كمعلمات.
⑤ قبل إيقاف تطبيق الويب أو إعادة تشغيله ، سيقوم محرك Servlet بإلغاء تثبيت Servlet ويطلق على طريقة Destroy () Servlet قبل إلغاء التثبيت.
3. مخطط استدعاء Servlet
4. تطوير servlet في Eclipse
قم بإنشاء مشروع ويب جديد في Eclipse ، وسيقوم Eclipse تلقائيًا بإنشاء بنية الدليل الموضحة في الشكل أدناه:
4.1. فئة تنفيذ واجهة Servlet
تحدد Servlet Interface Sun Company فئتين للتنفيذ الافتراضي ، وهما: genericservlet و httpservlet.
يشير HttPservlet إلى servlet يمكنه التعامل مع طلبات HTTP. يضيف بعض طرق معالجة بروتوكول HTTP إلى واجهة Servlet الأصلية ، والتي تعد أقوى من واجهة Servlet. لذلك ، عند كتابة Servlets ، يجب على المطورين عادة أن يرثوا هذا الفئة وتجنب تنفيذ واجهة Servlet مباشرة.
عندما يقوم HttPservlet بتنفيذ واجهة Servlet ، فإنه يكتب طريقة الخدمة. سيحدد الكود في هيئة الطريقة تلقائيًا طريقة طلب المستخدم. إذا كان طلب الحصول على الحصول على ، يتم استدعاء طريقة DOGE من httpservlet. إذا كان طلبًا آخر ، فسيتم استدعاء طريقة Dopost. لذلك ، عند كتابة servlets ، عادة ما يحتاج المطورون فقط إلى الكتابة فوق طريقة DOGET أو DOPOST بدلاً من الكتابة فوق طريقة الخدمة.
4.2. إنشاء وكتابة servlets من خلال Eclipse
حدد حزمة gacl.servlet.study ، انقر بزر الماوس الأيمن → جديد → servlet ، كما هو موضح في الشكل أدناه:
وبهذه الطريقة ، سوف نستخدم Eclipse لمساعدتنا في إنشاء servlet مع اسم ServletDemo1. سيكون هناك الرمز التالي في ServletDemo01:
package gacl.servlet.study ؛ استيراد java.io.ioException ؛ استيراد java.io.printwriter ؛ استيراد javax.servlet.servletexception ؛ استيراد javax.servlet.http.httpservlet ؛ استيراد javax.servlet.http.httplet javax.servlet.http.httpservletresponse ؛ الطبقة العامة servletdemo1 يمتد httpservlet { /*** طريقة doge من servlet. <br> * * يتم استدعاء هذه الطريقة عندما يكون للنموذج طريقة قيمة العلامة الخاصة به تساوي الحصول عليها. * * request request report report send by the client to the server * @param response repress recs by the client to the client * throws servletexception in regue regure att atthrows ioException إذا حدث خطأ */ public void doget (httpservled printWriter out = response.getWriter () ؛ out.println ("<! doctype html public/"-// w3c // dtd html 4.01 Transitional // en/">") ؛ Out.println ("<head> <title> servlet </title> </head>") ؛ out.println ("<body>") ؛ Out.print ("هذا هو") ؛ Out.print (this.getClass ()) ؛ out.println ("، باستخدام طريقة get") ؛ Out.println ("</body>") ؛ out.println ("</html>") ؛ out.flush () ؛ out.close () ؛ } /*** طريقة dopost من servlet. <br> * * يتم استدعاء هذه الطريقة عندما يكون للنموذج طريقة قيمة العلامة الخاصة به تساوي النشر. * * @param request the request send by the client to the server * @param response the response send by the server to the client * @throws ServletException if an error occurred * @throws IOException if an error occurred */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { استجابة. printWriter out = response.getWriter () ؛ out.println ("<! doctype html public/"-// w3c // dtd html 4.01 Transitional // en/">") ؛ out.println ("<html>") ؛ Out.println ("<head> <title> servlet </title> </head>") ؛ out.println ("<body>") ؛ out.println ("<body>") ؛ Out.println ("هذا هو") ؛ Out.print (this.getClass ()) ؛ Out.println ("، باستخدام طريقة البريد") ؛ Out.println ("</body>") ؛ out.println ("</html>") ؛ out.flush () ؛ out.close () ؛ }}يتم إنشاء هذه الرموز تلقائيًا بواسطة Eclipse ، وهناك زوجان من العلامات في ملف web.xml ، <servlet> </stervlet> و <servlet mapping> </servlet mapping>. يتم تكوين هذين الأزواج من العلامات مع servletdemo1 ، كما هو موضح في الشكل أدناه:
ثم يمكننا الوصول إلى servletdemo1 servlet من خلال المتصفح ، كما هو موضح في الشكل أدناه:
5. انتبه إلى التفاصيل في تطوير Servlet
5.1. Servlet Access URL Configuration
نظرًا لأن العميل يصل إلى الموارد في خادم الويب من خلال عنوان عنوان URL ، إذا كان برنامج Servlet يريد الوصول إليه بواسطة العالم الخارجي ، فيجب عليه تعيين برنامج Servlet إلى عنوان عنوان URL. يتم تنفيذ هذا العمل في ملف web.xml باستخدام عنصر <Servlet> وعنصر <Sradlet Mapping>.
يتم استخدام عنصر <Sradlet> لتسجيل Servlet. أنه يحتوي على عنصرين رئيسيين: <servlet-name> و <servlet-class> ، والتي يتم استخدامها لتعيين اسم تسجيل Servlet واسم الفصل الكامل للخدمة على التوالي.
يتم استخدام عنصر <stervlet Mapping> لرسم خريطة مسار وصول خارجي إلى servlet مسجلة. أنه يحتوي على عنصرين طفل: <servlet-name> و <url-pattern> ، والتي يتم استخدامها لتحديد اسم تسجيل servlet ومسار الوصول الخارجي للخدمة. على سبيل المثال:
<Srevlet> <Sradlet-name> servletdemo1 </rectlet-name> <Sradlet-class> gacl.servlet.study.servletdemo1 </revlet-class> </radlet> <cervlet mapping> <servlet-name> servletdemo1 </servlet-name> <Url-pattern> يمكن تعيين نفس servlet إلى عناوين URL متعددة ، أي أن قيمة الإعداد للعنصر الفردي <servlet-name> لعناصر <Creslet Mapping> المتعددة يمكن أن تكون اسم التسجيل لنفس servlet. على سبيل المثال: <Vervlet> <vervlet-name> servletdemo1 </stervlet-name> <sterlet-class> gacl.servlet.study.servletdemo1 </servlet-class> </servlet> <Servlet Mapping> <Servlet Mapping> <Url-pattern>/servlet/servletdemo1 </url-pattern> </revlet-mapping> <stervlet-mapping> <radlet-name> servletdemo1 </radlet-name> <Url-pattern> /1.htm </url-patern> </servlet-mapping> <Url-pattern> /2.jsp </url-pattern> </stervlet-mapping> <servlet-mapping> <sterled-mapping> <radlet-name> servletdemo1 </rectlet-name> <Url-pattern> /3.php </url-pattern> <Url-pattern> /4.aspx </url-pattern> </rectlet-mapping>
من خلال التكوين أعلاه ، عندما نريد الوصول إلى servlet مع اسم servletdemo1 ، يمكننا استخدام العناوين التالية للوصول:
http: // localhost: 8080/javaweb_servlet_study_20140531/servlet/servletdemo1
http: // localhost: 8080/javaweb_servlet_study_20140531/1.htm
http: // localhost: 8080/javaweb_servlet_study_20140531/2.jsp
http: // localhost: 8080/javaweb_servlet_study_20140531/3.php
http: // localhost: 8080/javaweb_servlet_study_20140531/4.aspx
يتم تعيين servletdemo1 إلى عناوين URL متعددة.
5.2. استخدم * رسم الخرائط للبطاقات البرية لعناوين URL للوصول إلى Servlet
يمكن أيضًا استخدام حرف*Wildcard في عناوين URL التي يقوم بها Servlet ، ولكن لا يمكن أن يكون هناك سوى تنسيقان ثابتان: أحدهما "*. التمديد" ، والآخر هو البدء بقطع (/) إلى الأمام وينتهي بـ "/*". على سبيل المثال:
<Sradlet> <Sradlet-Name> servletdemo1 </stervlet-name> <Servlet-Class> gacl.servlet.study.servletdemo1 </revlet-class> </radlet> </servlet mapping> <sterlet-name> servletdemo1 </servlet-name> <Url-pattern>
*يمكن أن يتطابق مع أي حرف ، بحيث يمكنك استخدام أي عنوان URL للوصول إلى servletdemo1 servlet ، كما هو موضح في الشكل أدناه:
لبعض علاقات التعيين أدناه:
خرائط servlet1 إلى /abc /*
خرائط Servlet2 إلى /*
خرائط Servlet3 إلى /ABC
خرائط servlet4 إلى *.do
سؤال:
عندما يكون عنوان URL للطلب هو "/abc/a.html" و "/ABC/*" و "/*" كلا المباراة ، فإن Servlet يستجيب لمحرك Servlet سيتصل servlet1.
عندما يكون عنوان URL للطلب هو "/ABC" ، فإن كل من "/ABC/*" و "/ABC" ، والذي يستجيب Servlet سيتصل بمحرك Servlet Servlet3.
عندما يكون عنوان URL للطلب هو "/abc/a.do" ، فإن كل من "/ABC/*" و "*.do" ، والذي يستجيب Servlet يستجيب لمحرك Servlet سيدعو Servlet1.
عندما يكون عنوان URL للطلب هو "/a.do" ، سيتصل كل من "/*" و "*.do" ، والتي يستجيب Servlet إلى محرك Servlet Servlet2.
عندما يكون عنوان URL للطلب هو "/xxx/yyy/a.do" ، كلاهما "/*" و "*.do" ، الذي يستجيب Servlet إلى Servlet Engine سيتصل Servlet2.
مبدأ المطابقة هو "من يبدو أكثر شبان سيجد من يبدو أكثر"
5.3. الفرق بين فصول Servlet و Java العادية
Servlet هو فئة Java للاتصال بواسطة برامج Java الأخرى (Servlet Engines). لا يمكن تشغيله بشكل مستقل ، ويتم التحكم بالكامل في تشغيله وجدولةه بواسطة محرك Servlet.
بالنسبة لطلبات Servlet المتعددة من العميل ، عادةً ما يقوم الخادم بإنشاء كائن مثيل Servlet فقط. وهذا يعني أنه بمجرد إنشاء كائن مثيل Servlet ، سيقيم في الذاكرة ويخدم طلبات أخرى لاحقة. لن يتم تدمير كائن مثيل Servlet حتى يخرج حاوية الويب.
خلال حياة Servlet بأكملها ، تسمى طريقة init الخاصة بـ Servlet مرة واحدة فقط. كل طلب وصول إلى servlet يتسبب في استدعاء محرك Servlet لطريقة خدمة Servlet مرة واحدة. لكل طلب وصول ، سيقوم محرك Servlet بإنشاء كائن طلب HTTPservletRequest جديد وكائن استجابة HTTPservletResponse جديد ، ثم تمرير هذين الكائنين كمعلمات إلى طريقة الخدمة () من servlet التي يتصل بها. ثم تستدعي طريقة الخدمة طريقة doxxx وفقًا لطريقة الطلب.
إذا تم تكوين عنصر <load-on-startup> في عنصر <Servlet> ، فعند بدء تشغيل تطبيق الويب ، فسيتم تحميله وإنشاء كائن مثيل Servlet واستدعاء طريقة init () لكائن مثيل servlet.
على سبيل المثال:
<Vervlet> <Sradlet-Name> invoker </servlet-name> <Servlet-class> org.apache.catalina.servlets.invokerservlet </servlet-class> <load-on-startup> 1 </load-on-startup> </servlet>
الغرض: اكتب initservlet لتطبيق الويب. تم تكوين هذا servlet للتحميل على بدء التشغيل لإنشاء جداول قاعدة بيانات ضرورية وبيانات لتطبيق الويب بأكمله.
5.4. servlet الافتراضي
إذا كان مسار تعيين servlet مجرد مقطع مائل للأمام (/) ، فإن هذا servlet يصبح الخدمة الافتراضية لتطبيق الويب الحالي.
أي عنوان URL لعنصر المطابقة <Servlet Mapping> الذي لا يمكن العثور عليه في ملف web.xml ، سيتم تسليم طلبات الوصول الخاصة بهم إلى servlet الافتراضي للمعالجة ، أي يتم استخدام servlet الافتراضي للتعامل مع طلبات الوصول التي لا تتم معالجتها بواسطة servlets الأخرى. على سبيل المثال:
<Srevlet> <vervlet-name> servletdemo2 </rectlet-name> <Servlet-class> gacl.servlet.study.servletdemo2 </servlet-class> <load-on-startup> 1 </load-on-startup> </qervlet> <! <Sradlet-Name> servletdemo2 </stervlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
عند الوصول إلى servlet غير موجود ، يتم استخدام servlet الافتراضي المكونة للمعالجة ، كما هو موضح في الشكل أدناه:
في ملف <tomcat تثبيت>/conf/web.xml ، يتم تسجيل servlet باسم org.apache.catalina.servlets.defaultservlet ، ويتم تعيين هذا servlet كخدم الافتراضي.
<Sradlet> <Sradlet-Name> افتراضي </servlet-name> <Srevlet-Class> org.apache.catalina.servlets.defaultservlet </radlet-class> <Ing-param> <Iram-name> debug </param-name> <Param-value> false </parm-value> </ith-param> <load-on-startup> 1 </load-on-startup> </servlet> <!-رسم الخرائط لـ servlet الافتراضي-> <Serlet Mapping> <Sradlet-Name> الافتراضي </servlet-name>
عند الوصول إلى ملف HTML الثابت والصورة في خادم Tomcat ، فأنت في الواقع تصل إلى هذا servlet الافتراضي.
5.5. مشكلات سلامة موضوع Servlet
عندما يصل العديد من العملاء إلى نفس servlet بشكل متزامن ، سيقوم خادم الويب بإنشاء مؤشر ترابط لطلب الوصول إلى كل عميل واتصل طريقة خدمة Servlet في هذا الموضوع. لذلك ، إذا تم الوصول إلى نفس المورد في طريقة الخدمة ، فقد يتسبب ذلك في مشاكل سلامة مؤشرات الترابط. على سبيل المثال ، الكود التالي:
الكود الذي لا يحتوي على مشكلات سلامة مؤشرات الترابط:
package gacl.servlet.study ؛ استيراد java.io.ioException ؛ استيراد javax.servlet.servletexception ؛ استيراد javax.servlet.http.httpservlet ؛ import javax.servlet.http.httpservlest ؛ javax.servlet.http.httpservletresponse ؛ public class servletdemo3 يمتد httpservlet {public void dogge (httpservletrequest request ، httpservletponse استجابة). يتم الوصول إلى المتغير I بشكل متزامن بواسطة مؤشرات ترابط متعددة ، ولكن لا توجد مشكلة سلامة مؤشر ترابط ، لأنني متغير محلي في طريقة DOGE. * عندما تصل مؤشرات الترابط المتعددة إلى طريقة DOGE بشكل متزامن ، يكون لكل مؤشر ترابط متغير خاص به ، * يقوم كل مؤشر ترابط بتشغيل متغير I ، لذلك لا توجد مشكلة في سلامة مؤشر ترابط * عندما يتم تحديد طريقة متعددة الخيوط طريقة معينة بشكل متزامن ، إذا كانت بعض الموارد (المتغيرات ، المجموعات ، وما إلى ذلك) يتم تعريفها داخل الطريقة * ثم كل مؤشر ترابط ، لذلك لا توجد مشكلة في سلامة الخيط */ int i = 1 ؛ i ++ ؛ response.getWriter (). الكتابة (i) ؛ } public void dopost (httpservletrequest request ، httpservletresponse) يلقي servletexception ، ioException {doget (request ، response) ؛ }} رمز مع مشكلات سلامة الموضوع:
حزمة gacl.servlet.study ؛ استيراد java.io.ioException ؛ استيراد javax.servlet.servletexception ؛ استيراد javax.servlet.http.httpservlet httpservlet {int i = 1 ؛ DOGED void public (طلب httpservletrequest ، استجابة httpservletresponse) يلقي servleTexception ، ioException {i ++ ؛ حاول {thread.sleep (1000*4) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } response.getWriter (). الكتابة (i+"") ؛ } public void dopost (httpservletrequest request ، httpservletresponse) يلقي servletexception ، ioException {doget (request ، response) ؛ }}تعريف أنا كمتغير عالمي. عندما يكون متغير الوصول إلى مؤشرات الترابط متعددة متزامنًا ، ستكون هناك مشاكل سلامة مؤشرات الترابط ، كما هو موضح في الشكل أدناه: قم بتشغيل متصفحين في نفس الوقت لمحاكاة الوصول المتزامن إلى نفس servlet. عادة ، يجب أن يرى المتصفح الأول 2 ، ويجب أن يرى المتصفح الثاني 3 ، لكن كلا المتصفحين يرون 3 ، وهو أمر غير طبيعي.
توجد مشكلات سلامة الموضوع فقط عندما تعمل مؤشرات ترابط متعددة نفس المورد بشكل متزامن. لذلك ، عند كتابة servlet ، إذا تم الوصول إلى مورد معين (متغير ، جمع ، إلخ) في وقت واحد ، ستكون هناك مشكلات سلامة مؤشرات الترابط. فكيف تحل هذه المشكلة؟
لنلقي نظرة على الكود التالي:
حزمة gacl.servlet.study ؛ استيراد java.io.ioException ؛ استيراد javax.servlet.servletexception ؛ استيراد javax.servlet.http.httpservlet httpservlet {int i = 1 ؛ DOGED VOID Public Void (طلب httpservletrequest ، استجابة httpservletresponse) يلقي servletexception ، ioException { /*** بعد إضافة مزامنة ، لا توجد مشكلة سلامة مؤشر ترابط عند الوصول المتزامن إلى i. * لماذا لا توجد مشكلة سلامة مؤشر ترابط بعد إضافة المزامنة؟ * إذا كان هناك مؤشر ترابط يصل إلى كائن Servlet الآن ، فسوف يحصل أولاً على قفل كائن Servlet* بعد تنفيذه ، فسيعود القفل إلى كائن Servlet. نظرًا لأنه يحصل أولاً على قفل كائن Servlet ، * لذلك عندما يقوم مؤشر ترابط آخر بالوصول إلى كائن Servlet ، نظرًا لأن القفل قد تم أخذه بعيدًا بواسطة مؤشر الترابط السابق ، يمكن للموضوع اللاحق الانتظار فقط في السطر */متزامن (هذا) {// في Java ، كل كائن يحتوي على قفل ، ويشير هذا إلى كائن Servlet I ++ ؛ حاول {thread.sleep (1000*4) ؛ } catch (interruptedException e) {E.PrintStackTrace () ؛ } response.getWriter (). الكتابة (i+"") ؛ }} public void dopost (httpservletrequest request ، httpservletresponse) يلقي servleTexception ، ioException {doget (request ، response) ؛ }}الآن هذا النهج هو إضافة قفل إلى كائن Servlet ، مع التأكد من أن مؤشر ترابط واحد فقط هو الوصول إلى الموارد في كائن Servlet في أي وقت ، لذلك لن تكون هناك مشكلات سلامة مؤشر ترابط ، كما هو موضح في الشكل أدناه:
على الرغم من أن هذا النهج يحل مشاكل سلامة الخيوط ، إلا أن كتابة Servlets يجب ألا تتعامل مع مشاكل سلامة الخيط بهذه الطريقة. إذا وصل 9999 شخصًا إلى Servlet في نفس الوقت ، فيجب على هؤلاء الأشخاص 9999 قائمة الانتظار للوصول إلى التسلسل.
استجابةً لمشكلة سلامة الخيط في Servlets ، توفر Sun حلاً: دع Servlet تنفيذ واجهة SingleThreadModel. إذا قام Servlet بتنفيذ واجهة SingleThreadModel ، فسيقوم محرك Servlet بالاتصال بأسلوب الخدمة الخاص به في وضع الخيوط الواحدة.
بالنظر إلى API Sevlet ، يمكنك أن ترى أن واجهة SingleThreadModel لا تحدد أي طرق أو ثوابت. في Java ، تسمى واجهة لا تحدد أي طرق أو ثوابت واجهة علامة. واحدة من أكثر واجهات العلامات النموذجية التي تراه غالبًا هي "قابلة للتسلسل". هذه الواجهة أيضًا لا تحدد أي طرق أو ثوابت. ما هو استخدام واجهات العلامات في جافا؟ الوظيفة الرئيسية هي وضع علامة على كائن وإخبار JVM بما يمكن أن يفعله هذا الكائن. على سبيل المثال ، يمكن تسلسل كائن الفئة التي تنفذ الواجهة "التسلسلية" ، وهناك أيضًا واجهة "مستنسخة" ، وهي أيضًا واجهة علامة. بشكل افتراضي ، لا يُسمح للكائنات في Java بالاستنساخ ، تمامًا مثل الأشخاص في الحياة الواقعية ، لا يُسمح بالاستنساخ ، ولكن طالما تم تنفيذ الواجهة "المستنسخة" ، يمكن استنساخ الكائن.
دع Servlet ينفذ واجهة SingleThreadModel ، فقط أضف الإعلان لتنفيذ واجهة SingleThreadModel لتعريف فئة Servlet.
بالنسبة إلى Servlets التي تنفذ واجهة SingleThreadModel ، لا يزال محرك Servlet يدعم الوصول المتزامن متعدد الخيوط إلى Servlet. تتمثل الطريقة في إنشاء كائنات مثيلات Servlet متعددة ، ويقوم كل مؤشر ترابط متزامن باستدعاء كائن مثيل Servlet مستقل بشكل منفصل.
لا يمكن لتنفيذ واجهة SingleThreadModel حل مشكلة سلامة مؤشرات الترابط في Servlets ، لأن محرك Servlet سيقوم بإنشاء كائنات مثيلات Servlet متعددة ، ويشير حل مشكلة السلامة متعددة الخيوط حقًا إلى المشكلة التي يطلق عليها كائن مثيل Servlet من قبل مؤشرات ترابط متعددة في نفس الوقت. في الواقع ، في Servlet API 2.4 ، تم تمييز SingleThreadModel على أنه تم إهماله (قديم).
ما سبق هو كل شيء عن هذا المقال ، آمل أن يكون مفيدًا لتعلم الجميع.