Skip to content

1. مقدمة

ملف PDF لهذا المستند متاح |هنا|.

الأمثلة الواردة في هذا المستند متاحة |هنا|.

نهدف هنا إلى تقديم المفاهيم الأساسية لـ Spring MVC، وهو إطار عمل ويب لـ Java يوفر بنية لتطوير تطبيقات الويب وفقًا لنمط MVC (Model–View–Controller)، وذلك من خلال أمثلة توضيحية. Spring MVC هو فرع من نظام Spring [http://projects.spring.io/spring-framework/]. كما نقدم محرك العرض Thymeleaf [http://www.thymeleaf.org/].

هذه الدورة مخصصة للقراء الذين يتمتعون بإجادة جيدة للغة Java. لا يلزم وجود معرفة مسبقة ببرمجة الويب.

على الرغم من تفصيل هذا المستند، إلا أنه من المحتمل أن يكون غير مكتمل. Spring هو إطار عمل واسع النطاق له العديد من الفروع. لمعرفة المزيد عن Spring MVC، يمكنك الرجوع إلى الموارد التالية:

تمت كتابة هذا المستند بحيث يمكن قراءته دون الحاجة إلى جهاز كمبيوتر. لذلك، تم تضمين العديد من لقطات الشاشة.

1.1. المصادر

يستند هذا المستند إلى مصدرين رئيسيين:

  • [مقدمة إلى ASP.NET MVC بالأمثلة]. Spring MVC و ASP.NET MVC هما إطاران متشابهان، وقد تم إنشاء الثاني بعد الأول بفترة طويلة. من أجل مقارنة الإطارين، اتبعت نفس التسلسل الوارد في الوثيقة الخاصة بـ ASP.NET MVC؛
  • لا يحتوي المستند الخاص بـ ASP.NET MVC حاليًا (ديسمبر 2014) على دراسة حالة مع حلها. وقد استخدمت هنا الدراسة الواردة في المستند [دليل AngularJS / Spring 4]، والتي قمت بتعديلها على النحو التالي:
    • دراسة الحالة في [AngularJS / Spring 4 Tutorial] هي دراسة لتطبيق عميل/خادم حيث يكون الخادم عبارة عن خدمة ويب / JSON تم إنشاؤها باستخدام Spring MVC ويكون العميل عميل AngularJS؛
    • في هذه الوثيقة، نستخدم نفس خدمة الويب/JSON، لكن العميل هو تطبيق ويب من مستويين [عميل jQuery] / [خدمة ويب/JSON

بالإضافة إلى هذه المصادر، بحثت في الإنترنت عن إجابات لأسئلتي. كان موقع الويب [http://stackoverflow.com/] مفيدًا لي بشكل خاص.

1.2. الأدوات المستخدمة

تم اختبار الأمثلة التالية في البيئة التالية:

  • جهاز يعمل بنظام Windows 8.1 Pro 64 بت؛
  • JDK 1.8؛
  • بيئة تطوير البرامج Spring Tool Suite 3.6.3 (انظر القسم 9.3
  • متصفح Chrome (لم يتم استخدام متصفحات أخرى)؛
  • ملحق Chrome [Advanced Rest Client] (انظر القسم 9.6

ملاحظة بخصوص JDK 1.8: تستخدم إحدى الطرق في دراسة الحالة طريقة من حزمة [java.lang] في Java 8.

جميع الأمثلة عبارة عن مشاريع Maven يمكن فتحها في Eclipse أو IntelliJ IDEA أو NetBeans. وفيما يلي، لقطات الشاشة مأخوذة من Spring Tool Suite IDE، وهو أحد أشكال Eclipse.

1.3. الأمثلة

الأمثلة متاحة |هنا| كملف ZIP قابل للتنزيل.

  

لتحميل جميع المشاريع إلى STS، اتبع الخطوات التالية:

  • في [1-3]، قم باستيراد مشاريع Maven؛
  • في [4]، حدد مجلد examples؛
  • في [5]، حدد جميع المشاريع الموجودة في المجلد؛
  • في [6]، قم بالتأكيد؛
  • في [7]، المشاريع المستوردة؛

1.4. دور Spring MVC في تطبيق الويب

دعونا نضع Spring MVC في سياق تطوير تطبيق ويب. في أغلب الأحيان، سيتم بناؤه على بنية متعددة المستويات مثل ما يلي:

  • الطبقة [Web] هي الطبقة التي تتعامل مع مستخدم تطبيق الويب. يتفاعل المستخدم مع تطبيق الويب من خلال صفحات الويب التي يتم عرضها في المتصفح. يقع Spring MVC في هذه الطبقة وفقط في هذه الطبقة؛
  • طبقة [الأعمال] تنفذ منطق الأعمال الخاص بالتطبيق، مثل حساب الراتب أو الفاتورة. تستخدم هذه الطبقة البيانات الواردة من المستخدم عبر طبقة [الويب] ومن نظام إدارة قواعد البيانات (DBMS) عبر طبقة [DAO
  • تدير طبقة [DAO] (كائنات الوصول إلى البيانات) وطبقة [ORM] (مُخطِط العلاقات بين الكائنات) ومحرك JDBC الوصول إلى البيانات في نظام إدارة قواعد البيانات. تعمل طبقة [ORM] كجسر بين الكائنات التي تتعامل معها طبقة [DAO] والصفوف والأعمدة في الجداول في قاعدة البيانات العلائقية. سنستخدم هنا ORM Hibernate. تسمح لنا مواصفة تسمى JPA (Java Persistence API) بالتجريد من ORM المحدد المستخدم، شريطة أن ينفذ هذه المواصفات. هذا هو الحال مع Hibernate و ORMs Java الأخرى. لذلك سنشير من الآن فصاعدًا إلى طبقة ORM باسم طبقة JPA؛
  • ويتم التعامل مع تكامل الطبقات بواسطة إطار عمل Spring؛

ستستخدم معظم الأمثلة الواردة أدناه طبقة واحدة فقط، وهي طبقة [Web]:

ومع ذلك، ستختتم هذه الوثيقة بإنشاء تطبيق ويب متعدد المستويات:

سيتصل المتصفح بتطبيق [Web1] تم تنفيذه باستخدام Spring MVC / Thymeleaf، والذي سيسترد بياناته من خدمة ويب [Web2] تم تنفيذها أيضًا باستخدام Spring MVC. وسيتصل هذا التطبيق الويب الثاني بقاعدة بيانات.

1.5. نموذج تطوير Spring MVC

يُنفذ Spring MVC نمط هندسة MVC (النموذج – العرض – وحدة التحكم) على النحو التالي:

تتم معالجة طلب العميل على النحو التالي:

  1. الطلب - تكون عناوين URL المطلوبة على النحو التالي: http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... يستخدم [Front Controller] ملف تكوين أو تعليقات Java لتوجيه الطلب إلى وحدة التحكم الصحيحة والإجراء الصحيح داخل تلك الوحدة. وللقيام بذلك، يستخدم حقل [Action] في عنوان URL. يتكون باقي عنوان URL [/param1/param2/...] من معلمات اختيارية سيتم تمريرها إلى الإجراء. يشير الحرف C في MVC هنا إلى السلسلة [Front Controller، Controller، Action]. إذا لم تتمكن أي وحدة تحكم من معالجة الإجراء المطلوب، فسيرد خادم الويب بأن عنوان URL المطلوب لم يتم العثور عليه.
  2. المعالجة
  • يمكن للإجراء المحدد استخدام المعلمات التي مررها إليه [Front Controller]. ويمكن أن تأتي هذه المعلمات من عدة مصادر:
    • مسار [/param1/param2/...] لعنوان URL،
    • معلمات [p1=v1&p2=v2] لعنوان URL،
    • من المعلمات التي أرسلها المتصفح مع طلبه؛
  • عند معالجة طلب المستخدم، قد يحتاج الإجراء إلى طبقة [business] [2b]. بمجرد معالجة طلب العميل، قد يؤدي ذلك إلى استجابات متنوعة. ومن الأمثلة الكلاسيكية على ذلك:
    • صفحة خطأ إذا تعذر معالجة الطلب بشكل صحيح
    • صفحة تأكيد في الحالات الأخرى
  • تقوم الإجراء بتوجيه عرض معين للعرض [3]. سيعرض هذا العرض البيانات المعروفة باسم نموذج العرض. هذا هو الحرف "M" في MVC. سيقوم الإجراء بإنشاء نموذج العرض هذا [2c] وتوجيه العرض للعرض [3]؛
  1. الاستجابة - تستخدم طريقة العرض المحددة V النموذج M الذي أنشأته الإجراء لتهيئة الأجزاء الديناميكية من استجابة HTML التي يجب إرسالها إلى العميل، ثم ترسل هذه الاستجابة.

الآن، دعونا نوضح العلاقة بين بنية الويب MVC والبنية الطبقية. اعتمادًا على كيفية تعريفنا للنموذج، قد يكون هذان المفهومان مرتبطين أو غير مرتبطين. لنفكر في تطبيق ويب Spring MVC أحادي الطبقة:

إذا قمنا بتنفيذ الطبقة [Web] باستخدام Spring MVC، فسنحصل بالفعل على بنية ويب MVC ولكن ليس بنية متعددة الطبقات. هنا، ستتولى الطبقة [Web] كل شيء: العرض، والمنطق التجاري، والوصول إلى البيانات. والأعمال هي التي ستقوم بتنفيذ هذا العمل.

الآن، دعونا ننظر في بنية ويب متعددة الطبقات:

يمكن تنفيذ طبقة [الويب] بدون إطار عمل وبدون اتباع نموذج MVC. وبذلك نحصل على بنية متعددة الطبقات، لكن طبقة الويب لا تنفذ نموذج MVC.

على سبيل المثال، في عالم .NET، يمكن تنفيذ طبقة [الويب] المذكورة أعلاه باستخدام ASP.NET MVC، مما ينتج عنه بنية متعددة الطبقات تحتوي على طبقة [ويب] بنمط MVC. وبمجرد الانتهاء من ذلك، يمكننا استبدال طبقة ASP.NET MVC هذه بطبقة ASP.NET كلاسيكية (WebForms) مع الحفاظ على بقية العناصر (منطق الأعمال، DAO، ORM) دون تغيير. وبذلك نحصل على بنية متعددة الطبقات مع طبقة [Web] لم تعد قائمة على MVC.

في MVC، قلنا إن نموذج M هو نموذج عرض V، أي مجموعة البيانات التي يعرضها عرض V. وهناك تعريف آخر لنموذج M في MVC:

يعتبر العديد من المؤلفين أن ما يقع على يمين طبقة [الويب] يشكل نموذج M في MVC. لتجنب الغموض، يمكننا الإشارة إلى:

  • نموذج المجال عند الإشارة إلى كل ما يقع على يمين طبقة [الويب]
  • نموذج العرض عند الإشارة إلى البيانات التي تعرضها طريقة العرض V

فيما يلي، سيشير مصطلح "نموذج M" حصريًا إلى نموذج عرض V.

1.6. أول مشروع Spring MVC

من الآن فصاعدًا، سنعمل مع بيئة تطوير البرامج (IDE) Spring Tool Suite (STS)، وهي نسخة من Eclipse مخصصة لـ Spring. يقدم موقع الويب [http://spring.io/guides] دروسًا تعليمية للبدء لاستكشاف نظام Spring البيئي. سنتبع إحدى هذه الدروس لتعلم تكوين Maven المطلوب لمشروع Spring MVC.

ملاحظة: لن يستوعب معظم المبتدئين تفاصيل المشروع بالكامل. وهذا ليس مهمًا. سيتم شرح هذه التفاصيل لاحقًا في هذا المستند. سنكتفي باتباع الخطوات.

1.6.1. مشروع العرض التوضيحي

  • في [1]، نقوم باستيراد أحد أدلة Spring؛
  • في [2]، نختار مثال [Serving Web Content
  • في [3]، نختار مشروع Maven؛
  • في [4]، نختار النسخة النهائية من الدليل؛
  • في [5]، نؤكد؛
  • في [6]، المشروع المستورد؛

دعونا نفحص المشروع، بدءًا من تكوين Maven الخاص به.

1.6.2. تكوين Maven

ملف [pom.xml] كما يلي:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>org.springframework</groupId>
    <artifactId>gs-serving-web-content</artifactId>
    <version>0.1.0</version>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
    </parent>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>
 
    <properties>
        <start-class>hello.Application</start-class>
    </properties>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
    <repositories>
        <repository>
            <id>spring-milestone</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>
 
    <pluginRepositories>
        <pluginRepository>
            <id>spring-milestone</id>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>
 
</project>
  • الأسطر 6–8: خصائص مشروع Maven. هناك علامة [<packaging>] تحدد نوع الملف الناتج عن عملية البناء في Maven مفقودة. وفي حالة عدم وجودها، يتم استخدام النوع [jar]. وبالتالي، فإن التطبيق هو تطبيق قابل للتنفيذ يعتمد على وحدة التحكم، وليس تطبيق ويب، حيث يكون التعبئة [war]؛
  • الأسطر 10–14: يحتوي مشروع Maven على مشروع أب [spring-boot-starter-parent]. يحدد هذا معظم تبعيات المشروع. قد تكون كافية، وفي هذه الحالة لا تضاف أي تبعيات إضافية، أو قد لا تكون كافية، وفي هذه الحالة تضاف التبعيات المفقودة؛
  • الأسطر 17-20: تتضمن الأداة [spring-boot-starter-thymeleaf] المكتبات اللازمة لمشروع Spring MVC المستخدم بالاقتران مع محرك عرض يسمى [Thymeleaf]. تتضمن هذه الأداة عددًا كبيرًا جدًا من المكتبات، بما في ذلك تلك الخاصة بخادم Tomcat المدمج. سيتم تشغيل التطبيق على هذا الخادم؛

المكتبات المضمنة في هذا التكوين عديدة:

أعلاه، نرى أرشيفات خادم Tomcat.

Spring Boot هو فرع من نظام Spring [http://projects.spring.io/spring-boot/]. يهدف هذا المشروع إلى تقليل التكوين المطلوب لمشاريع Spring إلى الحد الأدنى. لتحقيق ذلك، يقوم Spring Boot بالتكوين التلقائي بناءً على التبعيات الموجودة في مسار فئات المشروع. يوفر Spring Boot العديد من التبعيات الجاهزة للاستخدام. على سبيل المثال، تتضمن التبعية [spring-boot-starter-thymeleaf] الموجودة في مشروع Maven السابق جميع التبعيات المطلوبة لتطبيق Spring MVC الذي يستخدم محرك العرض [Thymeleaf]. وبفضل هاتين الميزتين:

  • التبعيات الجاهزة للاستخدام؛
  • التكوين التلقائي بناءً على هذه التبعيات والقيم الافتراضية "المعقولة"، يمكنك الحصول على تطبيق Spring MVC فعال بسرعة كبيرة. هذا هو الحال مع المشروع الذي تمت دراسته هنا؛

1.6.3. بنية تطبيق Spring MVC

يُنفذ Spring MVC نمط الهندسة المعمارية MVC (النموذج – العرض – وحدة التحكم):

تتم معالجة طلب العميل على النحو التالي:

  1. الطلب - تكون عناوين URL المطلوبة على النحو التالي: http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... [Dispatcher Servlet] هي فئة Spring التي تتعامل مع عناوين URL الواردة. وهي "توجه" عنوان URL إلى الإجراء الذي يجب أن يتعامل معه. هذه الإجراءات هي طرق لفئات محددة تسمى [Controllers]. الحرف C في MVC هنا هو السلسلة [Dispatcher Servlet، Controller، Action]. إذا لم يتم تكوين أي إجراء لمعالجة عنوان URL الوارد، فسيرد [Dispatcher Servlet] بأن عنوان URL المطلوب لم يتم العثور عليه (خطأ 404 NOT FOUND
  2. معالجة
  • يمكن للإجراء المحدد استخدام المعلمات التي مررها [Dispatcher Servlet] إليه. يمكن أن تأتي هذه المعلمات من عدة مصادر:
    • مسار [/param1/param2/...] لعنوان URL،
    • معلمات عنوان URL [p1=v1&p2=v2]،
    • من المعلمات التي أرسلها المتصفح مع طلبه؛
  • عند معالجة طلب المستخدم، قد يحتاج الإجراء إلى طبقة [الأعمال] [2b]. بمجرد معالجة طلب العميل، قد يؤدي ذلك إلى استجابات متنوعة. ومن الأمثلة الكلاسيكية على ذلك:
    • صفحة خطأ إذا تعذر معالجة الطلب بشكل صحيح
    • صفحة تأكيد في الحالات الأخرى
  • يُوجه الإجراء بعرض طريقة عرض محددة [3]. ستعرض طريقة العرض هذه البيانات المعروفة باسم نموذج العرض. هذا هو M في MVC. سيقوم الإجراء بإنشاء نموذج M هذا [2c] ويوجه بعرض طريقة عرض V [3]؛
  1. الاستجابة - تستخدم طريقة العرض V المحددة النموذج M الذي أنشأته الإجراء لتهيئة الأجزاء الديناميكية من استجابة HTML التي يجب إرسالها إلى العميل، ثم ترسل هذه الاستجابة.

سنقوم بفحص هذه العناصر المختلفة في المشروع قيد الدراسة.

1.6.4. وحدة التحكم C

  

يحتوي التطبيق المستورد على وحدة التحكم التالية:


package hello;
 
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
 
@Controller
public class GreetingController {
 
    @RequestMapping("/greeting")
    public String greeting(@RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
        model.addAttribute("name", name);
        return "greeting";
    }
 
}
  • السطر 8: تجعل العلامة [@Controller] فئة [GreetingController] وحدة تحكم Spring، مما يعني أن طرقها مسجلة لمعالجة عناوين URL. وحدة تحكم Spring هي كائن فريد. يتم إنشاء مثيل واحد فقط؛
  • السطر 11: تحدد العلامة [@RequestMapping] عنوان URL الذي تعالجه الطريقة، وهو في هذه الحالة عنوان URL [/greeting]. سنرى لاحقًا أنه يمكن تعيين معلمات لهذا العنوان URL وأنه من الممكن استرداد هذه المعلمات؛
  • السطر 12: تقبل الطريقة معلمتين:
    • [String name]: يتم تهيئة هذه المعلمة بواسطة معلمة تسمى [name] في الطلب المعالج، على سبيل المثال [/greeting?name=alfonse]. هذه المعلمة اختيارية [required=false] وعندما لا تكون موجودة، ستأخذ المعلمة [name] القيمة 'World' [defaultValue="World"
    • [Model model] هو نموذج عرض. يتم تمريره فارغًا، وتقع على عاتق الإجراء (طريقة greeting) مهمة ملئه. سيتم تمرير هذا النموذج إلى العرض الذي سيقوم الإجراء بعرضه. لذلك فهو نموذج عرض؛
  • السطر 13: يتم وضع قيمة [name] في نموذج العرض. تتصرف فئة [Model] كقاموس؛
  • السطر 14: تُرجع الطريقة اسم العرض الذي يجب أن يعرض النموذج الذي تم إنشاؤه. يعتمد الاسم الدقيق للعرض على تكوين [Thymeleaf]. في حالة عدم وجود مثل هذا التكوين، سيكون العرض المعروض هنا هو [/templates/greeting.html]، حيث يجب أن يكون المجلد [templates] في جذر مسار فئات المشروع؛

دعونا نفحص مشروع Eclipse الخاص بنا:

المجلدان [src/main/java] و [src/main/resources] هما مجلدان سيتم إضافة محتوياتهما إلى مسار فئات المشروع (Classpath). بالنسبة إلى [src/main/java]، سيتم وضع النسخ المُجمَّعة من كود مصدر Java هناك. أما محتويات المجلد [src/main/resources] فستُضاف إلى مسار فئات المشروع دون تعديل. وبالتالي، يمكننا أن نرى أن المجلد [templates] سيكون موجودًا في مسار فئات المشروع [1].

يمكنك التحقق من ذلك [2-3] في نافذة [Navigator] في Eclipse [Window / Show view / Other / General / Navigator]. يتم إنشاء المجلد [target] عن طريق ترجمة (أو "بناء") المشروع. يمثل المجلد [classes] جذر مسار الفئات. يمكنك ملاحظة أن المجلد [templates] موجود هناك.

1.6.5. الطريقة V

في MVC، رأينا للتو وحدة التحكم C والنموذج M. يتم تمثيل العرض V هنا بواسطة الملف [greeting.html] التالي:


<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <p th:text="'Hello, ' + ${name} + '!'" />
</body>
</html>
  • السطر 2: مساحة اسم علامة Thymeleaf؛
  • السطر 8: علامة <p> (فقرة) مع سمة Thymeleaf. تحدد السمة [th:text] محتوى الفقرة. داخل السلسلة، لدينا التعبير [${name}]. هذا يعني أننا نريد قيمة السمة [name] من قالب العرض. الآن، تذكر أن هذه السمة تمت إضافتها إلى النموذج بواسطة الإجراء:

model.addAttribute("name", name);

يحدد المعامل الأول اسم السمة، ويحدد الثاني قيمتها.

1.6.6. التنفيذ

  

تعد فئة [Application.java] هي الفئة القابلة للتنفيذ في المشروع. وفيما يلي شفرة البرمجة الخاصة بها:


package hello;
 
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
 
@ComponentScan
@EnableAutoConfiguration
public class Application {
 
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
 
}
  • السطر 11: الفئة قابلة للتنفيذ باستخدام طريقة [main] الخاصة بتطبيقات وحدة التحكم. ستقوم فئة [SpringApplication] في السطر 12 بتشغيل خادم Tomcat الموجود في التبعيات ونشر خدمة الويب عليه؛
  • السطر 4: يمكننا أن نرى أن فئة [SpringApplication] تنتمي إلى مشروع [Spring Boot
  • السطر 12: المعلمة الأولى هي الفئة التي تهيئ المشروع، والثانية تحتوي على أي معلمات إضافية؛
  • السطر 8: توجه العلامة [@EnableAutoConfiguration] Spring Boot لتكوين المشروع؛
  • السطر 7: تؤدي العلامة [@ComponentScan] إلى فحص الدليل الذي يحتوي على فئة [Application] بحثًا عن مكونات Spring. سيتم العثور على مكون واحد: فئة [GreetingController]، التي تحتوي على العلامة [@Controller]، مما يجعلها مكونًا من مكونات Spring؛

دعونا نقوم بتشغيل المشروع:

 

نحصل على سجلات وحدة التحكم التالية:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.9.RELEASE)

2014-11-27 16:48:12.567  INFO 3908 --- [           main] hello.Application                        : Starting Application on Gportpers3 with PID 3908 (started by ST in D:\data\istia-1415\spring mvc\dvp\gs-serving-web-content-complete)
2014-11-27 16:48:12.723  INFO 3908 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1a38c59b: startup date [Thu Nov 27 16:48:12 CET 2014]; root of context hierarchy
2014-11-27 16:48:13.813  INFO 3908 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2014-11-27 16:48:15.247  INFO 3908 --- [           main] .t.TomcatEmbeddedServletContainerFactory : Server initialized with port: 8080
2014-11-27 16:48:15.574  INFO 3908 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2014-11-27 16:48:15.575  INFO 3908 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/7.0.56
2014-11-27 16:48:15.955  INFO 3908 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2014-11-27 16:48:15.955  INFO 3908 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3236 ms
2014-11-27 16:48:16.918  INFO 3908 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2014-11-27 16:48:16.922  INFO 3908 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2014-11-27 16:48:17.354  INFO 3908 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-27 16:48:17.679  INFO 3908 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/greeting],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String hello.GreetingController.greeting(java.lang.String,org.springframework.ui.Model)
2014-11-27 16:48:17.681  INFO 3908 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2014-11-27 16:48:17.682  INFO 3908 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2014-11-27 16:48:17.696  INFO 3908 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-27 16:48:17.697  INFO 3908 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-27 16:48:18.159  INFO 3908 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2014-11-27 16:48:18.491  INFO 3908 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080/http
2014-11-27 16:48:18.493  INFO 3908 --- [           main] hello.Application                        : Started Application in 6.833 seconds (JVM running for 8.658)
  • السطر 13: يبدأ خادم Tomcat على المنفذ 8080 (السطر 12)؛
  • السطر 17: وجود سيرفلت [DispatcherServlet
  • السطر 20: تم اكتشاف الطريقة [hello.GreetingController.greeting]، إلى جانب عنوان URL الذي تعالجه [/greeting

لاختبار تطبيق الويب، نطلب عنوان URL [http://localhost:8080/greeting]:

 

قد يكون من المثير للاهتمام الاطلاع على رؤوس HTTP التي يرسلها الخادم. للقيام بذلك، سنستخدم المكون الإضافي لمتصفح Chrome المسمى [Advanced Rest Client] (انظر القسم 9.6):

  • في [1]، عنوان URL المطلوب؛
  • في [2]، يتم استخدام طريقة GET؛
  • في [3]، أشار الخادم إلى أنه يرسل استجابة بتنسيق HTML؛
  • في [4]، استجابة HTML؛
  • في [5]، نطلب نفس عنوان URL ولكن هذه المرة باستخدام طلب POST؛
  • في [7]، يتم إرسال المعلومات إلى الخادم بتنسيق [urlencoded
  • في [6]، المعلمة name مع قيمتها؛
  • في [8]، يُخبر المتصفح الخادم بأنه يرسل معلومات [urlencoded
  • في [9]، استجابة HTML من الخادم؛

لإيقاف التطبيق:

1.6.7. إنشاء أرشيف قابل للتنفيذ

من الممكن إنشاء أرشيف قابل للتنفيذ خارج Eclipse. توجد التهيئة اللازمة في ملف [pom.xml]:


    <properties>
        <start-class>hello.Application</start-class>
    </properties>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
</build>
  • تحدد الأسطر 7–10 المكون الإضافي الذي سيقوم بإنشاء الأرشيف القابل للتنفيذ؛
  • السطر 2 يحدد فئة الملف القابل للتنفيذ للمشروع؛

إليك كيفية المتابعة:

  • في [1]: نقوم بتشغيل هدف Maven؛
  • في [2]: هناك هدفان: [clean] لحذف مجلد [target] من مشروع Maven، و[package] لإعادة إنشائه؛
  • في [3]: سيتم إنشاء المجلد [target] الذي تم إنشاؤه في هذا المجلد؛
  • في [4]: تم إنشاء الهدف؛

ملاحظة: لكي ينجح الإنشاء، يجب أن تكون JVM المستخدمة بواسطة STS هي JDK [Window / Preferences / Java / Installed JREs]:

 

في السجلات التي تظهر في وحدة التحكم، من المهم رؤية المكون الإضافي [spring-boot-maven-plugin]. هذا هو المكون الإضافي الذي يقوم بإنشاء الأرشيف القابل للتنفيذ.

[INFO] --- spring-boot-maven-plugin:1.1.9.RELEASE:repackage (default) @ gs-serving-web-content ---

باستخدام وحدة التحكم، انتقل إلى المجلد الذي تم إنشاؤه:


gs-serving-web-content-complete\target>dir
 ...
 
 Répertoire de D:\data\istia-1415\spring mvc\dvp\gs-serving-web-content-complete
\target
 
27/11/2014  17:07    <DIR>          .
27/11/2014  17:07    <DIR>          ..
27/11/2014  17:07    <DIR>          classes
27/11/2014  17:07    <DIR>          generated-sources
27/11/2014  17:07        13 419 551 gs-serving-web-content-0.1.0.jar
27/11/2014  17:07             3 522 gs-serving-web-content-0.1.0.jar.original
27/11/2014  17:07    <DIR>          maven-archiver
27/11/2014  17:07    <DIR>          maven-status
  • السطر 12: الأرشيف الذي تم إنشاؤه؛

يتم تنفيذ هذا الأرشيف على النحو التالي:


gs-serving-web-content-complete\target>java -jar gs-serving-web-content-0.1.0.jar
 
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.9.RELEASE)
 
2014-11-27 17:14:50.439  INFO 8172 --- [           main] hello.Application                        : Starting Application on Gportpers3 with PID 8172 (D:\data\istia-1415\spring mvc\dvp\gs-serving-web-content-complete\target\gs-serving-web-content-0.1.0.jar started by ST in D:\data\istia-1415\spring mvc\dvp\gs-serving-web-content-complete\target)
2014-11-27 17:14:50.491  INFO 8172 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@12f4ec3a: startup date [Thu Nov 27 17:14:50 CET 2014]; root of context hierarchy

ملاحظة: يجب عليك أولاً إيقاف أي خدمة ويب قد تكون قد بدأت في Eclipse (انظر الصفحة 17).

الآن بعد أن أصبح تطبيق الويب قيد التشغيل، يمكنك الوصول إليه باستخدام متصفح:

 

1.6.8. نشر التطبيق على خادم Tomcat

على الرغم من أن Spring Boot مريح للغاية في وضع التطوير، إلا أن التطبيق النهائي سيتم نشره على خادم Tomcat حقيقي. وإليك كيفية القيام بذلك:

قم بتعديل ملف [pom.xml] على النحو التالي:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>org.springframework</groupId>
    <artifactId>gs-serving-web-content</artifactId>
    <version>0.1.0</version>
    <packaging>war</packaging>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
    </parent>
 
    <dependencies>
        <!-- thymeleaf environment -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- war generation -->
<!--         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency> -->
    </dependencies>
 
    <properties>
        <start-class>hello.Application</start-class>
    </properties>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
    <repositories>
        <repository>
            <id>spring-milestone</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>
 
    <pluginRepositories>
        <pluginRepository>
            <id>spring-milestone</id>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>
 
</project>

يجب إجراء التغييرات في مكانين:

  • السطر 9: يجب تحديد أنك ستقوم بإنشاء ملف WAR (أرشيف الويب)؛
  • الأسطر 24–28: يجب إضافة تبعية إلى عنصر [spring-boot-starter-tomcat]. يتضمن هذا العنصر جميع فئات Tomcat في تبعيات المشروع؛
  • السطر 27: هذه المكونة [متوفرة]، مما يعني أن الأرشيفات المقابلة لن يتم تضمينها في ملف WAR الذي تم إنشاؤه. بدلاً من ذلك، ستكون هذه الأرشيفات موجودة على خادم Tomcat حيث سيتم تشغيل التطبيق؛

في الواقع، إذا نظرنا إلى التبعيات الحالية للمشروع، نرى أن التبعية [spring-boot-starter-tomcat] موجودة بالفعل:

  

لذلك لا داعي لإضافته إلى ملف [pom.xml]. وقد قمنا بتعليقه للاسترشاد به.

نحتاج أيضًا إلى تكوين تطبيق الويب. في حالة عدم وجود ملف [web.xml]، يتم ذلك باستخدام فئة تمتد من [SpringBootServletInitializer]:

  

فيما يلي فئة [ApplicationInitializer]:


package hello;
 
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
 
public class ApplicationInitializer extends SpringBootServletInitializer {
 
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
 
}
  • السطر 6: فئة [ApplicationInitializer] تمتد من فئة [SpringBootServletInitializer
  • السطر 9: يتم تجاوز طريقة [configure] (السطر 8)؛
  • السطر 10: يتم توفير الفئة التي تقوم بتكوين المشروع؛

لتشغيل المشروع، اتبع الخطوات التالية:

  • في [1]، قم بتشغيل المشروع على أحد الخوادم المسجلة في بيئة تطوير Eclipse؛
  • في [2]، حدد [Tomcat v8.0] أعلاه؛

بمجرد الانتهاء من ذلك، يمكنك إدخال عنوان URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] في المتصفح:

 

ملاحظة: قد يفشل هذا التنفيذ اعتمادًا على إصدارات [Tomcat] و[TC Server Developer]. كان هذا هو الحال مع [Apache Tomcat 8.0.3 و8.0.15]، على سبيل المثال. في المثال أعلاه، كان إصدار Tomcat المستخدم هو [8.0.9].

نحن نعرف الآن كيفية إنشاء أرشيف WAR. وسنواصل العمل مع Spring Boot وأرشيف JAR القابل للتنفيذ الخاص به.

1.7. مشروع Spring MVC ثانٍ

1.7.1. مشروع العرض التوضيحي

  • في [1]، نقوم باستيراد أحد أدلة Spring؛
  • في [2]، نختار مثال [Rest Service
  • في [3]، نختار مشروع Maven؛
  • في [4]، نختار الإصدار النهائي من الدليل؛
  • في [5]، نؤكد؛
  • في [6]، المشروع المستورد؛

غالبًا ما تُسمى خدمات الويب التي يمكن الوصول إليها عبر عناوين URL قياسية والتي تُرجع بيانات JSON بخدمات REST (REpresentational State Transfer). في هذا المستند، سأشير ببساطة إلى الخدمة التي سنقوم ببنائها على أنها خدمة ويب/JSON. يُقال إن الخدمة تتبع نمط RESTful إذا كانت تتبع قواعد معينة. لم أحاول الالتزام بهذه القواعد.

دعونا الآن نفحص المشروع المستورد، بدءًا من تكوين Maven الخاص به.

1.7.2. تكوين Maven

ملف [pom.xml] هو كما يلي:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>org.springframework</groupId>
    <artifactId>gs-rest-service</artifactId>
    <version>0.1.0</version>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
    </parent>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
 
    <properties>
        <start-class>hello.Application</start-class>
    </properties>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
    <repositories>
        <repository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>
</project>
  • الأسطر 6–8: خصائص مشروع Maven. هناك علامة [<packaging>] مفقودة تحدد نوع الملف الناتج عن بناء Maven. في حالة عدم وجودها، يتم استخدام النوع [jar]. وبالتالي، فإن التطبيق هو تطبيق قابل للتنفيذ قائم على وحدة التحكم، وليس تطبيق ويب، وفي هذه الحالة سيكون التعبئة [war]؛
  • الأسطر 10-14: يحتوي مشروع Maven على مشروع أب [spring-boot-starter-parent]. يحدد هذا معظم تبعيات المشروع. قد تكون كافية، وفي هذه الحالة لا تضاف أي تبعيات إضافية، أو قد لا تكون كافية، وفي هذه الحالة تضاف التبعيات المفقودة؛
  • الأسطر 17-20: تتضمن الأداة [spring-boot-starter-web] المكتبات اللازمة لمشروع خدمة ويب Spring MVC حيث لا يتم إنشاء أي عروض. تتضمن هذه الأداة عددًا كبيرًا جدًا من المكتبات، بما في ذلك تلك الخاصة بخادم Tomcat المدمج. سيتم تشغيل التطبيق على هذا الخادم؛

المكتبات المضمنة في هذا التكوين عديدة:

أعلاه، نرى الأرشيفات الثلاثة لخادم Tomcat.

1.7.3. بنية خدمة Spring [web / JSON]

دعونا نستعرض كيف ينفذ Spring MVC نموذج MVC:

تتم معالجة طلب العميل على النحو التالي:

  1. الطلب - تكون عناوين URL المطلوبة على النحو التالي: http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&... [Dispatcher Servlet] هي فئة Spring التي تتعامل مع عناوين URL الواردة. وهي "توجه" عنوان URL إلى الإجراء الذي يجب أن يعالجه. هذه الإجراءات هي طرق لفئات محددة تسمى [Controllers]. الحرف C في MVC هنا هو السلسلة [Dispatcher Servlet، Controller، Action]. إذا لم يتم تكوين أي إجراء لمعالجة عنوان URL الوارد، فسيرد [Dispatcher Servlet] بأن عنوان URL المطلوب لم يتم العثور عليه (خطأ 404 NOT FOUND
  2. معالجة
  • يمكن أن تستخدم الإجراء المحدد المعلمات التي مررها [Dispatcher Servlet] إليه. يمكن أن تأتي هذه المعلمات من عدة مصادر:
    • مسار [/param1/param2/...] لعنوان URL،
    • معلمات عنوان URL [p1=v1&p2=v2]،
    • من المعلمات التي أرسلها المتصفح مع طلبه؛
  • عند معالجة طلب المستخدم، قد يحتاج الإجراء إلى طبقة [الأعمال] [2b]. بمجرد معالجة طلب العميل، قد يؤدي ذلك إلى استجابات متنوعة. ومن الأمثلة الكلاسيكية على ذلك:
    • صفحة خطأ إذا تعذر معالجة الطلب بشكل صحيح
    • صفحة تأكيد في الحالات الأخرى
  • تقوم الإجراء بتوجيه عرض معين للعرض [3]. سيعرض هذا العرض البيانات المعروفة باسم نموذج العرض. هذا هو M في MVC. سيقوم الإجراء بإنشاء نموذج M هذا [2c] وتوجيه عرض V للعرض [3]؛
  1. الاستجابة - تستخدم طريقة العرض V المحددة النموذج M الذي أنشأته الإجراء لتهيئة الأجزاء الديناميكية من استجابة HTML التي يجب إرسالها إلى العميل، ثم ترسل هذه الاستجابة.

بالنسبة لخدمة الويب / JSON، يتم تعديل البنية السابقة بشكل طفيف:

  • في [4a]، يتم تحويل النموذج، وهو فئة Java، إلى سلسلة JSON بواسطة مكتبة JSON؛
  • في [4b]، يتم إرسال سلسلة JSON هذه إلى المتصفح؛

1.7.4. وحدة التحكم C

  

يحتوي التطبيق المستورد على وحدة التحكم التالية:


package hello;
 
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
public class GreetingController {
 
    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();
 
    @RequestMapping("/greeting")
    public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
}
  • السطر 9: تجعل العلامة التوضيحية [@RestController] فئة [GreetingController] وحدة تحكم Spring، مما يعني أن طرقها مسجلة لمعالجة عناوين URL. لقد رأينا العلامة التوضيحية المماثلة [@Controller]. كان نوع الإرجاع لطرق تلك الوحدة هو [String]، وهو اسم العرض المراد عرضه. هنا، الأمر مختلف. تُرجع أساليب [@RestController] كائنات يتم تسلسلها لإرسالها إلى المتصفح. يعتمد نوع التسلسل الذي يتم إجراؤه على تكوين Spring MVC. هنا، سيتم تسلسلها إلى JSON. إن وجود مكتبة JSON في تبعيات المشروع هو ما يجعل Spring Boot يقوم تلقائيًا بتكوين المشروع بهذه الطريقة؛
  • السطر 14: تحدد العلامة [@RequestMapping] عنوان URL الذي تعالجه الطريقة، وهو في هذه الحالة عنوان URL [/greeting
  • السطر 15: سبق أن شرحنا التعليق التوضيحي [@RequestParam]. والنتيجة التي ترجعها الطريقة هي كائن من النوع [Greeting].
  • السطر 12: عدد صحيح طويل من النوع الذري. وهذا يعني أنه يدعم الوصول المتزامن. قد ترغب خيوط متعددة في زيادة متغير [counter] في نفس الوقت. وسيتم التعامل مع هذا بشكل صحيح. لا يمكن للخيط قراءة قيمة العداد إلا بعد أن ينتهي الخيط الذي يقوم بتعديله حاليًا من تعديله.

1.7.5. نموذج M

نموذج M الناتج عن الطريقة السابقة هو كائن [Greeting] التالي:

  

package hello;
 
public class Greeting {
 
    private final long id;
    private final String content;
 
    public Greeting(long id, String content) {
        this.id = id;
        this.content = content;
    }
 
    public long getId() {
        return id;
    }
 
    public String getContent() {
        return content;
    }
}

سيؤدي تحويل هذا الكائن إلى JSON إلى إنشاء السلسلة {"id":n,"content":"text"}. وفي النهاية، ستكون سلسلة JSON الناتجة عن طريقة وحدة التحكم بالصيغة التالية:

{"id":2,"content":"Hello, World!"}

أو

{"id":2,"content":"Hello, John!"}

1.7.6. التنفيذ

  

تعد فئة [Application.java] هي الفئة القابلة للتنفيذ في المشروع. وفيما يلي نصها البرمجي:


package hello;
 
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
 
@ComponentScan
@EnableAutoConfiguration
public class Application {
 
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
 
}

لقد سبق أن تناولنا هذا الرمز وشرحناه في المثال السابق.

1.7.7. تشغيل المشروع

دعونا نقوم بتشغيل المشروع:

 

نحصل على سجلات وحدة التحكم التالية:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.9.RELEASE)

2014-11-28 15:22:55.005  INFO 3152 --- [           main] hello.Application                        : Starting Application on Gportpers3 with PID 3152 (started by ST in D:\data\istia-1415\spring mvc\dvp-final\gs-rest-service)
2014-11-28 15:22:55.046  INFO 3152 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@62e136d3: startup date [Fri Nov 28 15:22:55 CET 2014]; root of context hierarchy
2014-11-28 15:22:55.762  INFO 3152 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2014-11-28 15:22:56.567  INFO 3152 --- [           main] .t.TomcatEmbeddedServletContainerFactory : Server initialized with port: 8080
2014-11-28 15:22:56.738  INFO 3152 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2014-11-28 15:22:56.740  INFO 3152 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/7.0.56
2014-11-28 15:22:56.869  INFO 3152 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2014-11-28 15:22:56.870  INFO 3152 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1827 ms
2014-11-28 15:22:57.478  INFO 3152 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2014-11-28 15:22:57.481  INFO 3152 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2014-11-28 15:22:57.685  INFO 3152 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-28 15:22:57.879  INFO 3152 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/greeting],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public hello.Greeting hello.GreetingController.greeting(java.lang.String)
2014-11-28 15:22:57.884  INFO 3152 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2014-11-28 15:22:57.885  INFO 3152 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2014-11-28 15:22:57.906  INFO 3152 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-28 15:22:57.907  INFO 3152 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-11-28 15:22:58.231  INFO 3152 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2014-11-28 15:22:58.318  INFO 3152 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080/http
2014-11-28 15:22:58.319  INFO 3152 --- [           main] hello.Application                        : Started Application in 3.788 seconds (JVM running for 4.424)
  • السطر 13: يبدأ خادم Tomcat على المنفذ 8080 (السطر 12)؛
  • السطر 17: وجود سيرفلت [DispatcherServlet
  • السطر 20: تم اكتشاف الطريقة [GreetingController.greeting

لاختبار تطبيق الويب، نطلب عنوان URL [http://localhost:8080/greeting]:

 

نتلقى سلسلة JSON المتوقعة.

ملاحظة: لم يعمل هذا المثال مع المتصفح المدمج في Eclipse.

قد يكون من المفيد عرض رؤوس HTTP المرسلة من الخادم. للقيام بذلك، سنستخدم المكون الإضافي لـ Chrome المسمى [Advanced Rest Client] (انظر الملاحق، القسم 9.6):

  • في [1]، عنوان URL المطلوب؛
  • في [2]، يتم استخدام طريقة GET؛
  • في [3]، استجابة JSON؛
  • في [4]، أشار الخادم إلى أنه يرسل استجابة بتنسيق JSON؛
  • في [5]، نطلب نفس عنوان URL ولكن هذه المرة باستخدام طلب POST؛
  • في [7]، يتم إرسال المعلومات إلى الخادم بتنسيق [urlencoded
  • في [6]، المعلمة name مع قيمتها؛
  • في [8]، يُخبر المتصفح الخادم بأنه يرسل بيانات [urlencoded
  • في [9]، رد JSON من الخادم؛

1.7.8. إنشاء أرشيف قابل للتنفيذ

كما فعلنا في المشروع السابق، نقوم بإنشاء أرشيف قابل للتنفيذ:

  • في [1]: نقوم بتشغيل هدف Maven؛
  • في [2]: هناك هدفان: [clean] لحذف مجلد [target] من مشروع Maven، و[package] لإعادة إنشائه؛
  • في [3]: سيكون المجلد [target] الذي تم إنشاؤه موجودًا في هذا المجلد؛
  • في [4]: نقوم بإنشاء الهدف؛

في السجلات التي تظهر في وحدة التحكم، من المهم رؤية المكون الإضافي [spring-boot-maven-plugin]. هذا هو المكون الإضافي الذي يقوم بإنشاء الأرشيف القابل للتنفيذ.

[INFO] --- spring-boot-maven-plugin:1.1.0.RELEASE:repackage (default) @ gs-rest-service ---

باستخدام وحدة التحكم، انتقل إلى المجلد الذي تم إنشاؤه:


D:\Temp\wksSTS\gs-rest-service\target>dir
 ...
11/06/2014  15:30    <DIR>          classes
11/06/2014  15:30    <DIR>          generated-sources
11/06/2014  15:30        11 073 572 gs-rest-service-0.1.0.jar
11/06/2014  15:30             3 690 gs-rest-service-0.1.0.jar.original
11/06/2014  15:30    <DIR>          maven-archiver
11/06/2014  15:30    <DIR>          maven-status
...
  • السطر 5: الأرشيف الذي تم إنشاؤه؛

يتم تنفيذ هذا الأرشيف على النحو التالي:


D:\Temp\wksSTS\gs-rest-service-complete\target>java -jar gs-rest-service-0.1.0.jar
 
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.0.RELEASE)
 
2014-06-11 15:32:47.088  INFO 4972 --- [           main] hello.Application
                  : Starting Application on Gportpers3 with PID 4972 (D:\Temp\wk
sSTS\gs-rest-service-complete\target\gs-rest-service-0.1.0.jar started by ST in
D:\Temp\wksSTS\gs-rest-service-complete\target)
...

ملاحظة: يجب عليك أولاً إيقاف أي خدمة ويب قد تكون قد تم تشغيلها في Eclipse (انظر القسم 1.6.6).

الآن بعد أن أصبح تطبيق الويب قيد التشغيل، يمكنك الوصول إليه باستخدام متصفح:

 

1.7.9. نشر التطبيق على خادم Tomcat

كما فعلنا في المشروع السابق، نقوم بتعديل ملف [pom.xml] على النحو التالي:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>org.springframework</groupId>
    <artifactId>gs-rest-service</artifactId>
    <version>0.1.0</version>
    <packaging>war</packaging>
 
    ...
</project>
  • السطر 9: يجب تحديد أنك ستقوم بإنشاء ملف WAR (أرشيف ويب)؛

كما تحتاج إلى تكوين تطبيق الويب. إذا لم يكن هناك ملف [web.xml]، يتم ذلك باستخدام فئة تمتد من [SpringBootServletInitializer]:

  

فيما يلي فئة [ApplicationInitializer]:


package hello;
 
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
 
public class ApplicationInitializer extends SpringBootServletInitializer {
 
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
 
}
  • السطر 6: فئة [ApplicationInitializer] تمتد من فئة [SpringBootServletInitializer
  • السطر 9: يتم تجاوز طريقة [configure] (السطر 8)؛
  • السطر 10: يتم توفير الفئة التي تقوم بتكوين المشروع؛

لتشغيل المشروع، اتبع الخطوات التالية:

  • في [1-2]، قم بتشغيل المشروع على أحد الخوادم المسجلة في بيئة تطوير Eclipse؛

بمجرد الانتهاء من ذلك، يمكنك طلب عنوان URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] في متصفح:

 

1.8. الخلاصة

لقد قدمنا نوعين من مشاريع Spring MVC:

  • مشروع يرسل فيه تطبيق الويب دفق HTML إلى المتصفح. يتم إنشاء هذا الدفق بواسطة محرك العرض [Thymeleaf
  • مشروع يرسل فيه تطبيق الويب دفق JSON إلى المتصفح؛

في الحالة الأولى، يلزم وجود تبعيتين من Maven للمشروع:


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
    </parent>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
</dependencies>

في الحالة الثانية، تكون تبعيات Maven كما يلي:


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.1.9.RELEASE</version>
    </parent>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
</dependencies>

تؤدي هذه التكوينات إلى ظهور عدد كبير من التبعيات المتتالية، وكثير منها غير ضروري. لنشر التطبيق، سنستخدم تكوينًا يدويًا لـ Maven يتضمن فقط التبعيات الضرورية للمشروع.

سنعود الآن إلى أساسيات برمجة الويب من خلال تقديم مفهومين أساسيين:

  • تبادل HTTP (بروتوكول نقل النص التشعبي) بين المتصفح وتطبيق الويب؛
  • لغة HTML (لغة ترميز النص التشعبي) التي يفسرها المتصفح لعرض الصفحة التي تلقاها؛