محتوى:
مقدمة
بدأت التعرف على التقنيات والأطر الجديدة بالنسبة لي من خلال دراسة الأمثلة المختلفة التي تم استخدامها فيها، لأنني عادةً ما أفهم شيئًا ما بشكل أفضل عندما أراه عمليًا باستخدام مثال لتطبيق كامل. عادةً ما تكون هذه الأمثلة هي تطبيقات CRUD ( C create، و Read ، و U pdate، و D elete)، والإنترنت مليء بمثل هذه الأمثلة بدرجات متفاوتة من التعقيد. المشكلة هي أنهم عادة لا يشرحون بالتفصيل كيف وماذا ولماذا تم القيام به هناك، ولماذا تمت إضافة كذا وكذا، ولماذا هناك حاجة إلى كذا وكذا، وما إلى ذلك. في معظم الحالات، يأخذون تطبيقًا مكتملًا بالكامل، مع ملف POM نهائيًا، مع الإصدارات النهائية من الفئات، ويقومون ببساطة بتشغيل كل منها، دون التركيز على الأشياء الصغيرة التي ربما تبدو واضحة لشخص ذي خبرة. لقد ألقيت نظرة على الكثير من هذه الأمثلة وعادة ما يكون من الواضح كيف يعمل كل شيء، ولكن كيف وصلوا إلى هذا ليس واضحًا تمامًا. لذلك، قررت أن مثل هذا المثال سيكون مفيدًا، ليس من موقف مطور ذي خبرة، ولكن من موقف مبتدئ لم يتعامل أبدًا مع الربيع والإسبات وأشياء أخرى.إنشاء مشروع
لذا، بما أنني مبتدئ، فلن نستخدم أي نماذج أولية غامضة. لا تزال أداة تهيئة الربيع تبدو مخيفة للغاية. لذلك، سنقوم بإنشاء مشروع Maven البسيط الأكثر عادية. ليس لدي اسم نطاق، لذا سأكتب في groupid فقطtestgroup
، وفي artifactid سأكتب الاسم، على سبيل المثال، filmography
(ستكون هذه قائمة بالأفلام). نحن ننشئ مشروعًا ونختار Enable auto-import
متى تقترحه الفكرة. بفضل هذا، في كل مرة نقوم فيها بإجراء أي تغييرات على ملف POM (نموذج كائن المشروع، يصف هذا الملف البنية الكاملة لمشروع Maven)، سيتم تطبيق كل شيء تلقائيًا على المشروع على الفور. سيتم أخذ المكتبات من مستودعنا المحلي إذا كانت لدينا بالفعل، أو إذا استخدمنا بعض التبعيات الجديدة التي لم نتعامل معها من قبل، فسيقوم Maven ببساطة بتنزيلها عبر الإنترنت من المستودع المركزي. لدى Maven أيضًا وظيفة تنزيل المصادر والوثائق (تنزيل المصادر و/أو الوثائق). كما أنها مريحة جدًا، إذا كان هناك شيء غير واضح مع فئة أو طريقة ما، فيمكنك الانتقال إلى الكود المصدري ومعرفة كيفية عمل كل شيء بالداخل. دعونا نضيف بعض التفاصيل. سيكون هذا تطبيق ويب وسنستخدم Tomcat . لنشر تطبيق على Tomcat، تحتاج إلى نقله إلى هناك في شكل أرشيف حربي (موارد تطبيقات الويب، تنسيق خاص لتطبيقات الويب). للقيام بذلك، قم بإضافة السطر التالي إلى ملف POM بحيث يتم تجميع التطبيق في أرشيف الحرب:
<packaging>war</packaging>
حسنًا، ستحتاج أيضًا إلى دليل خاص لمصادر الويب، وفي حالتنا ستكون هناك صفحات jsp وبعض موارد الويب. لنقم بإنشاء main
دليل webapp
. يجب أن يُطلق عليه هذا الاسم بالضبط وأن يكون موجودًا بنفس main
الطريقة تمامًا مثل java
، resources
لأن هذا هو هيكل دليل Maven القياسي. بمجرد تثبيت الحزمة war
وبالتالي تحديد أن هذا مشروع ويب، webapp
سيتم وضع علامة على الدليل تلقائيًا كمصادر لتطبيقات الويب (ستكون هناك نقطة زرقاء عليه) وسيتم البحث في كل ما يتعلق بالويب في هذا المجلد. ولحظة واحدة. افتراضيًا، يستخدم Maven إصدار اللغة 1.5، لكنني أريد، على سبيل المثال، استخدام الإصدار 1.8 - Java 8 (يمكنك أن تأخذ 10 أو 11، ولكن لا توجد خطط لاستخدام أي ميزات من هناك، لذا فليكن 8) ). يمكن حل هذه المشكلة بكل بساطة، نكتب في Google شيئًا مثل "Maven java 8" ونرى ما يجب إضافته إلى ملف POM حتى يقوم Maven بتجميع فئاتنا للإصدار المطلوب. ونتيجة لذلك، لدينا ما يلي:
اتصال الربيع MVC
عليك أن تبدأ في مكان ما. وفقًا للخطة، سنقوم بتوصيل قاعدة البيانات واستخدام السبات، لكن كل هذا يبدو مخيفًا بعض الشيء في الوقت الحالي. نحن بحاجة إلى القيام بشيء أبسط أولاً. SpringMVC، هذا أفضل بالفعل، لقد كنا على دراية بنمط MVC لفترة طويلة، وقد تم استخدامه في نصف المهام الكبيرة للدورة. ومن هنا سنبدأ بالرقص. لإنشاء تطبيق ويب باستخدام Spring MVC، نحتاج أيضًا إلى Servlet-API، أي. هذا الشيء الذي سيتم من خلاله التفاعل بين الطلب والاستجابة. دعونا نحاول ربط هذا. نذهب إلى Google، ونبحث عن التبعيات الضرورية في مستودع Maven ونضيفها إلى ملفpom.xml
. في قسم المكتبات الخارجية، يمكنك أن ترى أنه لم يتم تحميل Spring-webmvc فحسب ، بل تم أيضًا تحميل مجموعة من الأشياء الأخرى. أولئك. لا نحتاج إلى إضافة تبعيات لـ Spring Core ، والسياق ، والفاصوليا ، وما إلى ذلك. التي نحتاجها، تم سحب كل ما نحتاجه مع Spring-webmvc .
نحن بحاجة إلى تقديم إخلاء صغير. يوصى عادة بإضافة تبعية بشكل منفصل لكل مكتبة مستخدمة، حتى لو كانت مجمعة بالفعل مع تلك المضافة بالفعل، لأن وهذا يمكن أن يساعد في تجنب بعض المشاكل ومواطن الخلل. مثال بسيط. لنفترض أننا أضفنا تبعية تستخدم بعض واجهات برمجة التطبيقات (API)، وفي الوقت نفسه ستقوم بسحب نوع من التنفيذ لواجهة برمجة التطبيقات (API) هذه. ثم أضفنا تبعية أخرى تستخدم نفس واجهة برمجة التطبيقات (API) ونقوم أيضًا بسحب بعض تطبيقاتها لهذا الغرض، ولكن الأمر مختلف هذه المرة. وبالتالي، سيكون لدينا تطبيقان مختلفان لنفس واجهة برمجة التطبيقات. وإذا أردنا نحن أنفسنا استخدام بعض أساليب واجهة برمجة التطبيقات هذه في مكان ما، فستنشأ مشكلة، لأن النظام لن يعرف التطبيق الذي سيستخدمه، وسيختار بشكل عشوائي، ربما ليس التطبيق الذي توقعناه. وإذا قمت بتحديد تبعية لأحد التطبيقات بشكل صريح، فسيتم إعطاء الأولوية لها. ومع ذلك، هذه ليست توصية صارمة، فهي تنطبق بشكل أساسي على المشاريع الكبيرة حيث يتم استخدام العديد من المكتبات المختلفة من شركات مختلفة. لن نفعل ذلك هنا، حتى لا يتم تحميل ملف POM أكثر من اللازم، ولا يتوقع حدوث مشكلات. ولكن مع ذلك، لا يزال الأمر يستحق أن نأخذ ذلك في الاعتبار. |
provided
يعتمد javax.servlet-api
؟ النطاق هو نطاق التبعية، provided
مما يعني أن التبعية ستكون متاحة في مرحلة تجميع التطبيق واختباره، ولكن لن يتم أرشفته. الحقيقة هي أنه لنشر التطبيق، سنستخدم حاوية servlet، Tomcat، وهي تحتوي بالفعل على مثل هذه المكتبات بداخلها، لذلك ليست هناك حاجة لنقلها إلى هناك وإثقال كاهل الأرشيف بتحميل غير ضروري. وبالنظر إلى المستقبل، لنفس السبب سنستغني عن الطريقة المعتادة main
، لأنها موجودة بالفعل داخل Tomcat.
إنشاء الصفحات ووحدة التحكم
دعونا نحاول طهي شيء بسيط الآن. أولاً، لنقم بإنشاءwebapp
دليل إضافي، على سبيل المثال pages
، حيث سيتم تخزين وجهات نظرنا، أي. صفحات jsp، وإنشاء بضع صفحات. سنحتاج إلى صفحة تعرض فيها في المستقبل قائمة الأفلام مثلا films.jsp
، وربما يمكننا عمل صفحة منفصلة للمونتاج، فليكن editPage.jsp
. لن نقوم بملئها بأي شيء جدي في الوقت الحالي، فقط للاختبار، سنقوم بإنشاء رابط من صفحة إلى أخرى. الآن نحن بحاجة إلى فئة من شأنها معالجة الطلبات، أي. مراقب. دعونا نضيف حزمة جديدة controller
وننشئ فئة فيها FilmController
(بشكل عام، ليس من الضروري تجميع كل شيء في حزم مختلفة، سيكون هذا التطبيق صغيرًا وبسيطًا جدًا، ولكن في المشروع العادي يمكن أن يكون هناك العديد من وحدات التحكم وفئات التكوين والنماذج وما إلى ذلك، لذا حتى البدء بالمشاريع الصغيرة، فمن الأفضل أن تعتاد فورًا على القيام بكل شيء بطريقة منظمة ومنظمة حتى لا تكون هناك فوضى). سنقوم في هذا الفصل بإنشاء طرق تعيد وجهات نظرنا استجابةً للطلبات.
package testgroup.filmography.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class FilmController {
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView allFilms() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("films");
return modelAndView;
}
@RequestMapping(value = "/edit", method = RequestMethod.GET)
public ModelAndView editPage() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("editPage");
return modelAndView;
}
}
ما هي النقطة؟ يحتوي SpringMVC على شيء يسمى DispatcherServlet
. وهذا يشبه وحدة التحكم الرئيسية، حيث تمر جميع الطلبات الواردة من خلالها ثم تقوم بتمريرها إلى وحدة تحكم معينة. يخبر التعليق التوضيحي @Controller
Spring MVC أن هذه الفئة عبارة عن وحدة تحكم (حسنًا، منطقية بشكل عام)، وسيقوم المرسل بفحص التعليقات التوضيحية @RequestMapping
لاستدعاء الطريقة المناسبة. يسمح لك التعليق التوضيحي @RequestMapping
بتحديد عناوين طرق التحكم، والتي ستكون متاحة من خلالها في العميل (المتصفح). ويمكن أيضًا تطبيقه على فئة وحدة التحكم لتعيين، إذا جاز التعبير، عنوان الجذر لجميع الأساليب. تم تعيين allFilms()
معلمة الطريقة على " "، لذلك سيتم استدعاؤها فورًا عند إدخال المجموعة http://host:port/ في المتصفح (أي، بشكل افتراضي هي http://localhost:8080/ أو http //127.0 .0.1:8080/ ). تحدد المعلمة نوع الطلب المدعوم (GET، POST، PUT، وما إلى ذلك). نظرًا لأننا هنا نتلقى البيانات فقط، فسيتم استخدام GET. لاحقًا، عندما تظهر طرق الإضافة والتحرير، ستكون هناك بالفعل طلبات POST. (بالمناسبة، بدلاً من التعليقات التوضيحية التي تشير إلى الطريقة، يمكنك استخدام التعليقات التوضيحية ، وما إلى ذلك بشكل متساوٍ )). في أساليبنا، نقوم بإنشاء كائن وتعيين اسم العرض الذي يجب إرجاعه. value
/
method
@RequestMapping
@GetMapping
@PostMapping
@GetMapping
@RequestMapping(method = RequestMethod.GET
ModelAndView
إعدادات
دعنا ننتقل إلى إعداد التكوين.config
لنقم بإنشاء فصل دراسي في الحزمة WebConfig
. سيكون لها طريقة واحدة فقط تُرجع كائنًا من النوع ViewResolver
، وهي الواجهة الضرورية للعثور على تمثيل بالاسم.
package testgroup.filmography.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "testgroup.filmography")
public class WebConfig {
@Bean
ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
@Configuration
يخبر Spring أن هذه الفئة هي فئة تكوين وتحتوي على تعريفات وتبعيات bean
المكونات. الفاصوليا هي كائنات يديرها Spring. يتم استخدام التعليق التوضيحي لتعريف الفول @Bean
. @EnableWebMvc
يسمح لك باستيراد تكوين Spring MVC من ملف WebMvcConfigurationSupport
. يمكنك أيضًا تنفيذ، على سبيل المثال، واجهة WebMvcConfigurer
تحتوي على مجموعة كاملة من الأساليب، وتخصيص كل شيء حسب رغبتك، لكننا لسنا بحاجة إلى الخوض في ذلك بعد، فالإعدادات القياسية ستكون كافية. @ComponentScan
يخبر Spring أين يبحث عن المكونات التي يجب أن يديرها، على سبيل المثال. الفئات المميزة بتعليق توضيحي @Component
أو مشتقاته مثل @Controller
, @Repository
, @Service
. تحدد هذه التعليقات التوضيحية فول الفصل تلقائيًا. في هذه الطريقة، viewResolver()
نقوم بإنشاء تنفيذه وتحديد المكان الذي نبحث فيه بالضبط عن التمثيلات في webapp
. لذلك، عندما نقوم بتعيين الاسم " films
" في طريقة التحكم، سيتم العثور على العرض كـ " /pages/films.jsp
" لذا، لدينا فئة تكوين، ولكنها في الوقت الحالي مجرد نوع من الفئات المنفصلة، ولا تؤثر على تطبيقنا بأي شكل من الأشكال. . نحن بحاجة إلى تسجيل هذا التكوين في سياق الربيع. لهذا تحتاج إلى فئة AbstractAnnotationConfigDispatcherServletInitializer
. في الحزمة، config
نقوم بإنشاء خليفتها، مثل AppInitializer ، وننفذ أساليبها.
package testgroup.filmography.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[0];
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
الطريقة الأخيرة تسجل العناوين وهناك طريقتان أخريان لتسجيل فئات التكوين. يتم وضع تكوينات الويب، حيث ViewResolver
يتم تعريف 's وما شابه ذلك، في getServletConfigClasses()
. من الأفضل أن تقرأ عن كل هذا في الوثائق والأدلة المختلفة، ولكن في حالتنا ليس من الضروري الخوض في هذا بعد، فمن حيث WebConfig
المبدأ، يمكن RootClasses
تعريفنا في كليهما، بل يمكنك تحديد كليهما في وقت واحد، وسيظل يعمل . شيء اخر. قد تكون هناك مشاكل في الترميز عندما تكون النتيجة خربشات عند إرسال القيم بأحرف روسية من النموذج. لحل هذه المشكلة، سنضيف مرشحًا لمعالجة الطلبات مسبقًا. نذهب إلى فئة AppInitializer ونتجاوز الطريقة getServletFilters
التي نشير فيها إلى الترميز المطلوب، بالطبع، يجب أن يكون هو نفسه كما هو الحال في أي مكان آخر، كما هو الحال في الصفحات وفي قاعدة البيانات:
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceEncoding(true);
return new Filter[] {characterEncodingFilter};
}
حسنًا، يبدو أن كل شيء قد تم إعداده، ويمكنك محاولة تشغيله ومعرفة ما سيحدث. تشغيل -> تشغيل -> تحرير التكوينات -> إضافة تكوين جديد -> خادم Tomcat -> محلي . بعد ذلك، تحتاج إلى تحديد قطعة أثرية لنشرها. ستعطي الفكرة نفسها تحذيرًا : لم يتم وضع علامة على أي عناصر للنشر . انقر فوق زر الإصلاح وحدد ...: انفجرت الحرب . أو يمكنك الانتقال إلى النشر -> إضافة -> قطعة أثرية -> ...: انفجرت الحرب . وتحتاج أيضًا إلى الانتقال إلى النشر وتعيين حقل سياق Applecation (سيكون هذا جزءًا من عنوان URL حيث سيكون التطبيق متاحًا في المتصفح) على " /
". بعد ذلك، سيكون تطبيقنا متاحًا على الفور على http://localhost:8080/ (ولكن يمكنك أيضًا تحديد شيء ما هناك، على سبيل المثال " /filmography
"، وبعد ذلك ستحتاج فقط إلى إضافة هذا إلى جميع العناوين، أي على سبيل المثال لن يكون هناك " http://localhost:8080/edit" ، لكنه سيكون "http://localhost:8080/filmography/edit" ). انقر فوق "تشغيل" وانتظر حتى يبدأ. وإليك ما حصلت عليه: يبدو أن كل شيء على ما يرام، ولكن هناك تحذير واحد. الحقيقة هي أن صفحاتنا متاحة الآن للعامة ويمكن الوصول إليها مباشرة عن طريق كتابة المسار في شريط العناوين. ندخل إلى http://localhost:8080/pages/films.jsp والآن استلمنا صفحتنا دون علم المراقب. بطريقة ما هذا ليس صحيحًا تمامًا، لذلك سنقوم بإنشاء webapp
دليل خاص WEB-INF
. سيتم إخفاء ما بداخله عن الجمهور ولا يمكن الوصول إليه إلا من خلال وحدة التحكم. نضع الدليل مع وجهات نظرنا ( pages
) في WEB-INF
، ViewResolver
وبالتالي نضيفه إلى البادئة:
viewResolver.setPrefix("/WEB-INF/pages/");
الآن نحصل على صفحتنا على http://localhost:8080 ، ولكن إذا حاولنا مباشرة إلى http://localhost:8080/WEB-INF/pages/films.jsp فسنحصل على خطأ 404. حسنًا، رائع، لدينا أبسط تطبيق ويب، Hello World كما يقولون. يبدو هيكل المشروع حاليًا كما يلي:
نموذج
لدينا بالفعل طرق عرض ووحدة تحكم، ولكن في MVC يوجد أيضًا حرف ثالث، لذا لإكمال الصورة سنضيف أيضًا نموذجًا. في الحزمة،model
لنقم بإنشاء فصل Film
، على سبيل المثال، باستخدام الحقول التالية: int id
و String title
(العنوان) و int year
(سنة الإصدار) و String genre
(النوع) و boolean watched
(أي هل شاهدت هذا الفيلم بالفعل أم لا).
package testgroup.filmography.model;
public class Film {
private int id;
private String title;
private int year;
private String genre;
private boolean watched;
// + Getters and setters
}
لا يوجد شيء خاص، مجرد فئة عادية، وحقول خاصة، وحاصلون على الحروف، ومستوطنون. تسمى كائنات هذه الفئات أيضًا POJO
(Plain Old Java Object)، حسنًا، أي. "كائن جافا بسيط". دعونا نحاول الآن إنشاء مثل هذا الكائن وعرضه على الصفحة. في الوقت الحالي، لن نقلق كثيرًا بشأن كيفية إنشائه وتهيئته. لتجربتها، دعونا بغباء نقوم بإنشائها مباشرة في وحدة التحكم، على سبيل المثال، مثل هذا:
public class FilmController {
private static Film film;
static {
film = new Film();
film.setTitle("Inception");
film.setYear(2010);
film.setGenre("sci-fi");
film.setWatched(true);
}
وأضف هذا الكائن إلى كائننا ModelAndView
باستخدام الطريقة addObject
:
@RequestMapping(method = RequestMethod.GET)
public ModelAndView allFilms() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("films");
modelAndView.addObject("film", film);
return modelAndView;
}
الآن يمكننا عرض هذا الكائن على صفحتنا. بدلاً films.jsp
من Hello World، سنكتب وسيتم استبدال ${film}
الكائن المطابق لاسم السمة " " هنا. film
دعونا نحاول تشغيله ونرى ما حدث (للحصول على إخراج واضح للكائن، Film
تمت إعادة تعريف الفئة toString()
):
تحكم عرض نموذج
في هذه المرحلة، يبدو أن لدينا بالفعل تطبيق SpringMVC متكامل. قبل المضي قدمًا، سيكون من الجيد إلقاء نظرة على كل شيء مرة أخرى ومعرفة كيفية عمل كل شيء. يمكنك العثور على العديد من الصور والرسوم البيانية على الإنترنت حول هذا الموضوع، أحب هذه الصورة:Dispatcher Servlet
، ثم يتم العثور على وحدة تحكم مناسبة لمعالجة هذا الطلب باستخدام HandlerMapping
(هذه واجهة لاختيار وحدة التحكم، والتحقق من أي من وحدات التحكم المتاحة لديها طريقة تقبل مثل هذا العنوان) ، يستدعي طريقة مناسبة ويعيد Controller
معلومات حول العرض، ثم يعثر المرسل على العرض المطلوب بالاسم باستخدام ViewResolver
'a، وبعد ذلك يتم نقل بيانات النموذج إلى هذا العرض ونحصل على صفحتنا كمخرجات. شيء من هذا القبيل. يتبع... تقديم Maven، Spring، MySQL، Hibernate وأول تطبيق CRUD (الجزء 1) تقديم Maven، Spring، MySQL، Hibernate وأول تطبيق CRUD (الجزء 2) تقديم Maven، Spring، MySQL، Hibernate و أول تطبيق CRUD (الجزء 3) مقدمة إلى Maven وSpring وMySQL وHbernate وأول تطبيق CRUD (الجزء 4)
GO TO FULL VERSION