4. تشغيل التطبيق
نريد الآن تشغيل التطبيق خارج بيئة تطوير STS (للخادم) و WebStorm (للعميل).
4.1. نشر خدمة الويب على خادم Tomcat
لقد رأينا في القسم 2.11.9 كيفية إنشاء أرشيف WAR لـ Tomcat. نكرر العملية هنا. أولاً، للحفاظ على الهيكل الحالي، نقوم بنسخ مشروع Eclipse [rdvmedecins-webapi-v3] إلى [rdvmedecins-webapi-v4].
![]() |
تم تعديل ملف [pom.xml] على النحو التالي:
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st.spring4.mvc</groupId>
<artifactId>rdvmedecins-webapi-v4</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>rdvmedecins-webapi-v3</name>
<description>Gestion de RV Médecins</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.0.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>istia.st.spring4.rdvmedecins</groupId>
<artifactId>rdvmedecins-metier-dao-v2</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
يجب إجراء التغييرات في مكانين:
- السطر 5: يجب تحديد أنك ستقوم بإنشاء ملف WAR (Web ARchive)؛
- الأسطر 23–27: يجب إضافة تبعية إلى عنصر [spring-boot-starter-tomcat]. يتضمن هذا العنصر جميع فئات Tomcat في تبعيات المشروع؛
- السطر 26: هذه المكونة [متوفرة]، مما يعني أن الملفات المقابلة لن يتم تضمينها في ملف WAR الذي تم إنشاؤه. بدلاً من ذلك، ستكون هذه الملفات موجودة على خادم Tomcat حيث سيتم تشغيل التطبيق؛
يجب عليك أيضًا تكوين تطبيق الويب. في حالة عدم وجود ملف [web.xml]، يتم ذلك باستخدام فئة تمتد من [SpringBootServletInitializer]:
![]() |
فيما يلي فئة [ApplicationInitializer]:
package rdvmedecins.web.config;
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(AppConfig.class);
}
}
- السطر 6: فئة [ApplicationInitializer] تمتد من فئة [SpringBootServletInitializer]؛
- السطر 8: يتم تجاوز طريقة [configure] (السطر 7)؛
- السطر 9: يتم توفير فئة [AppConfig] لتكوين المشروع؛
بمجرد الانتهاء من ذلك، قد تحتاج إلى تحديث مشروع Maven (اضطررت إلى القيام بذلك): [انقر بزر الماوس الأيمن على المشروع / Maven / تحديث المشروع] أو [Alt-F5].
لتشغيل المشروع، اتبع الخطوات التالية:
![]() |
- في [1]، قم بتشغيل المشروع على أحد الخوادم المسجلة في بيئة تطوير Eclipse؛
- في [2]، حدد [tc Server Developer]، وهو الخيار الافتراضي. هذا هو أحد أشكال Tomcat؛
ستحصل على النتيجة التالية:
![]() |
هذا أمر طبيعي. تذكر أن خدمة الويب لا تحتوي على عنوان URL [/] في أساليبها. عند تجربة عنوان URL [/getAllMedecins]، ستحصل على الاستجابة التالية:
![]() |
هذا أمر طبيعي. خدمة الويب محمية.
الآن دعونا نطلق العميل [rdvmedecins-angular-v2] في WebStorm:
![]() |
في [1]، أدخل عنوان URL لخدمة الويب الجديدة [http://localhost:8080/rdvmedecins-webapi-v4]. نحصل على النتيجة التالية:

لتشغيل التطبيق خارج بيئة تطوير STS، هناك حلول متنوعة. وإليك أحدها.
قم بتنزيل إصدار من Tomcat [http://tomcat.apache.org/download-80.cgi] (يوليو 2014):
![]() |
اختر نسخة مضغوطة في [1] وقم بفك ضغطها في [2]. ارجع إلى STS:
![]() |
- في علامة التبويب [الخوادم]، انقر بزر الماوس الأيمن على تطبيق [rdvmedecins-webapi-v4] واختر خيار [استعراض موقع النشر]؛
- في [4]: انسخ المجلد [rdvmedecins-webapi-v4]؛
![]() |
- في [5]، الصق المجلد [rdvmedecins-webapi-v4] في مجلد Tomcat [webapps]؛
- في [6]، قم بتشغيل ملف الأوامر [startup.bat] (يجب إيقاف خادم Tomcat المدمج في STS). تفتح نافذة DOS لعرض سجلات Tomcat. يجب أن تظهر هذه السجلات أن تطبيق [rdvmedecins-webapi-v4] قد تم تشغيله.
للتحقق من ذلك، قم بتشغيل عميل Angular [rdvmedecins-angular-v2] مرة أخرى في WebStorm:
![]() |
في [1]، أدخل عنوان URL لخدمة الويب الجديدة [http://localhost:8080/rdvmedecins-webapi-v4]. سترى النتيجة التالية:

4.2. نشر عميل Angular على خادم Tomcat
الآن بعد أن تم نشر خدمة الويب على Tomcat، سنقوم بنشر عميل Angular على الخادم أيضًا. يمكن أن يكون هذا هو الخادم الذي يستضيف خدمة الويب بالفعل. سنتبع هذا النهج.
أولاً، نقوم بنسخ العميل [rdvmedecins-angular-v2] إلى [rdvmedecins-angular-v3] ونجري التغييرات التالية:
![]() |
- في [1]، تم نقل كل شيء إلى مجلد باسم [ app]؛
- في [1]، قمنا بإزالة المجلد [bower-components]، الذي كان يحتوي على مكتبات CSS و JS المختلفة المطلوبة للمشروع. تم نسخ جميع هذه العناصر إلى المجلد [lib] [2]؛
- في [1]، تم تغيير اسم ملف [app.html] إلى [index.html]؛
تم تعديل ملف [index.html] لمراعاة التغييرات في مسارات الموارد المستخدمة:
<!DOCTYPE html>
<html ng-app="rdvmedecins">
<head>
<title>RdvMedecins</title>
...
<!-- on CSS -->
...
<link href="lib/bootstrap-theme.min.css" rel="stylesheet"/>
<link href="lib/bootstrap-select.min.css" rel="stylesheet"/>
</head>
<!-- controller [appCtrl], model [app] -->
<body ng-controller="appCtrl">
<div class="container">
...
</div>
<!-- Bootstrap core JavaScript ================================================== -->
<script type="text/javascript" src="lib/jquery.min.js"></script>
<script type="text/javascript" src="lib/bootstrap.min.js"></script>
<script type="text/javascript" src="lib/bootstrap-select.min.js"></script>
<script type="text/javascript" src="lib/footable.js"></script>
<!-- angular js -->
<script type="text/javascript" src="lib/angular.min.js"></script>
<script type="text/javascript" src="lib/ui-bootstrap-tpls.min.js"></script>
<script type="text/javascript" src="lib/angular-route.min.js"></script>
<script type="text/javascript" src="lib/angular-translate.min.js"></script>
<script type="text/javascript" src="lib/angular-base64.min.js"></script>
<!-- modules -->
...
<!-- services -->
...
<!-- guidelines -->
...
<!-- controllers -->
....
</body>
</html>
بالإضافة إلى ذلك، تم تعديل وحدة التحكم [loginCtrl] لتشير إلى الخادم الصحيح حتى لا يضطر المستخدم إلى كتابة عنوان URL:
// credentials
app.serverUrl = "http://localhost:8080/rdvmedecins-webapi-v4";
app.username = "admin";
app.password = "admin";
الآن بعد الانتهاء من ذلك، دعونا نقوم بتشغيل ملف [index.html]:
![]() | ![]() |
ثم دعونا نسجل الدخول إلى خدمة الويب. من المفترض أن تعمل. بمجرد التحقق من ذلك، دعونا نوقف خادم Tomcat. سنعيد استخدام خادم STS المدمج.
في STS، انسخ محتويات مجلد [rdvmedecins-angular-v3/app] بالكامل إلى مجلد [webapp] في مشروع [rdvmedecins-webapi-v4] (علامة التبويب Navigator) [1]:
![]() |
بمجرد الانتهاء من ذلك، قم بتشغيل [2] خادم STS VMware، ثم اطلب عنوان URL [http://localhost:8080/rdvmedecins-webapi-v4/app/index.html]:
![]() |
نواجه مشكلة في الأذونات عند [3]. وهذا ليس مفاجئًا نظرًا لأننا قمنا بتأمين خدمة الويب. نحتاج إلى تحديد أن الوصول إلى الملف [/app/index.html] غير مقيد. لنعد إلى Eclipse:
![]() |
تذكر أن أذونات الوصول تم تعريفها في فئة [SecurityConfig]. دعونا نعدلها على النحو التالي:
@Override
protected void configure(HttpSecurity http) throws Exception {
// CSRF
http.csrf().disable();
// the password is transmitted by the header Authorization: Basic xxxx
http.httpBasic();
// the HTTP OPTIONS method must be authorized for all
http.authorizeRequests() //
.antMatchers(HttpMethod.OPTIONS, "/", "/**").permitAll();
// the [app] folder is accessible to all
http.authorizeRequests() //
.antMatchers(HttpMethod.GET, "/app", "/app/**").permitAll();
// only the ADMIN role can use the application
http.authorizeRequests() //
.antMatchers("/", "/**") // all URL
.hasRole("ADMIN");
}
- السطران 11–12: نمنح الجميع إذنًا لقراءة المجلد [app] ومحتوياته. للقيام بذلك، نتبع المثال الموضح في الأسطر السابقة.
الآن، أعد تشغيل خادم STS Tomcat واطلب عنوان URL [http://localhost:8080/rdvmedecins-webapi-v4/app/index.html] مرة أخرى:

هذه المرة، يعمل الأمر.
4.3. رؤوس CORS
قد تتذكر أننا واجهنا صعوبة كبيرة في التعامل مع رؤوس CORS. في المثال السابق:
- تقع خدمة الويب على عنوان URL [http://localhost:8080/rdvmedecins-webapi-v4]؛
- يوجد عميل HTML على عنوان URL [http://localhost:8080/rdvmedecins-webapi-v4/app]؛
وبالتالي، فإن عميل HTML وخدمة الويب موجودان على نفس الخادم [http://localhost:8080]. لا توجد تعارضات CORS لأن هذه التعارضات تحدث فقط عندما لا يكون العميل والخادم في نفس المجال. يجب أن نتمكن من التحقق من ذلك. دعونا نعود إلى STS:
![]() |
يتم التحكم في إنشاء رؤوس CORS من خلال متغير منطقي محدد في فئة [ApplicationModel]:
// données de configuration
private boolean CORSneeded = true;
نقوم بتعيين القيمة المنطقية أعلاه إلى false، ونعيد تشغيل خدمة الويب، ونطلب عنوان URL [http://localhost:8080/rdvmedecins-webapi-v4/app/index.html] مرة أخرى. يمكننا أن نرى أن التطبيق يعمل.
4.4. نشر عميل Angular على جهاز لوحي يعمل بنظام Android
تتيح لك أداة [Phonegap] [http://phonegap.com/] إنشاء ملف قابل للتنفيذ على الأجهزة المحمولة (Android، iOS، Windows 8، إلخ) من تطبيق HTML/JS/CSS. هناك طرق مختلفة لتحقيق ذلك. سنستخدم أبسطها: أداة عبر الإنترنت متوفرة على موقع Phonegap [http://build.phonegap.com/apps].
![]() |
- قبل [1]، قد تحتاج إلى إنشاء حساب؛
- في [1]، ابدأ؛
- في [2]، اختر خطة مجانية تسمح بتطبيق Phonegap واحد فقط؛
![]() |
- في [3]، قم بتنزيل التطبيق المضغوط [4] (المجلد [app] الذي تم إنشاؤه في القسم 4.2 مضغوط)؛
![]() |
- في [5]، قم بتسمية التطبيق؛
- في [6]، قم بإنشائه. قد يستغرق ذلك دقيقة واحدة. انتظر حتى تشير أيقونات المنصات المحمولة المختلفة إلى اكتمال الإنشاء؛
![]() |
- تم إنشاء ملفات Android [7] و Windows [8] الثنائية فقط؛
- انقر على [7] لتنزيل الملف الثنائي لنظام Android؛
![]() |
- في [9]، الملف الثنائي [apk] الذي تم تنزيله؛
قم بتشغيل محاكي [GenyMotion] لجهاز لوحي يعمل بنظام Android (انظر القسم 6.4):
![]() |
أعلاه، نقوم بتشغيل محاكي جهاز لوحي مع Android API 16. بمجرد تشغيل المحاكي،
- قم بإلغاء قفله عن طريق سحب القفل (إن وجد) إلى الجانب ثم تركه؛
- باستخدام الماوس، اسحب ملف [PGBuildApp-debug.apk] الذي قمت بتنزيله وأسقطه على المحاكي. سيتم بعد ذلك تثبيته وتشغيله؛
![]() |
تحتاج إلى تغيير عنوان URL إلى [1]. للقيام بذلك، في نافذة موجه الأوامر، اكتب الأمر [ipconfig] (السطر 1 أدناه)، والذي سيعرض عناوين IP المختلفة لجهازك:
C:\Users\Serge Tahé>ipconfig
Configuration IP de Windows
Carte réseau sans fil Connexion au réseau local* 15 :
Statut du média. . . . . . . . . . . . : Média déconnecté
Suffixe DNS propre à la connexion. . . :
Carte Ethernet Connexion au réseau local :
Suffixe DNS propre à la connexion. . . : ad.univ-angers.fr
Adresse IPv6 de liaison locale. . . . .: fe80::698b:455a:925:6b13%4
Adresse IPv4. . . . . . . . . . . . . .: 172.19.81.34
Masque de sous-réseau. . . . . . . . . : 255.255.0.0
Passerelle par défaut. . . . . . . . . : 172.19.0.254
Carte réseau sans fil Wi-Fi :
Statut du média. . . . . . . . . . . . : Média déconnecté
Suffixe DNS propre à la connexion. . . :
...
قم بتدوين عنوان IP لشبكة Wi-Fi (الأسطر 6–9) أو عنوان IP للشبكة المحلية (الأسطر 11–17). ثم استخدم عنوان IP هذا في عنوان URL لخادم الويب:
![]() |
بمجرد الانتهاء من ذلك، قم بالاتصال بخدمة الويب:
![]() |
اختبر التطبيق على المحاكي. من المفترض أن يعمل. على جانب الخادم، يمكنك السماح أو عدم السماح برؤوس CORS في فئة [ApplicationModel]:
// données de configuration
private boolean CORSneeded = false;
هذا الأمر غير ذي صلة بتطبيق Android. فهو لا يعمل في متصفح. متطلبات رأس CORS تأتي من المتصفح، وليس من الخادم.
4.5. نشر عميل Angular على محاكي هاتف ذكي يعمل بنظام Android
نكرر الخطوة السابقة باستخدام محاكي هاتف ذكي. نريد التحقق من كيفية عمل عميلنا على الشاشات الصغيرة:
![]() |
- في [1]، نقوم بتشغيل محاكي الهاتف الذكي؛
- في [2] و[3]، تم طي شريط التنقل ليصبح قائمة؛
![]() |
- في [4]، نقوم بتسجيل الدخول؛
- في [5]، يتم عرض القائمة والتقويم أحدهما أسفل الآخر بدلاً من عرضهما جنباً إلى جنب؛
![]() |
- في [6]، نطلب التقويم؛
- في [7]، نظرًا لصغر حجم الشاشة، يتم إخفاء جزء من الفترات الزمنية. وقد تعاملت مكتبة [footable] مع هذا الأمر؛
![]() |
- في [8]، نفس العرض كما في السابق، ولكن هذه المرة مع موعد.
بشكل عام، تتكيف تطبيقنا بشكل جيد مع الهاتف الذكي. يمكن بالتأكيد تحسينه، لكنه يظل قابلاً للاستخدام.





























