Skip to content

17. المثال 15 – تكامل Struts 2 / Spring

في المثال السابق، لم تكن لدينا بيانات ذات نطاق Application مشتركة بين جميع الطلبات لجميع المستخدمين. نعرض مثالاً على ذلك هنا. لتنفيذ ذلك، نستخدم إطار العمل Spring [http://www.springsource.org/]. Spring أداة ذات قيمة كبيرة جدًا. ولن نعرض في هذا المثال سوى جزء ضئيل جدًّا منه. وسيستخدمه مثال لاحق بشكل أكثر كثافة.

الهدف من هذا التطبيق هو عرض البيانات ذات النطاق Application.

17.1. مشروع NetBeans

مشروع NetBeans الخاص بالتطبيق هو كما يلي:

  • في [1]:
  • [web.xml] الذي يقوم بتكوين تطبيق الويب سيتطور مقارنةً بما كان عليه في الإصدارات السابقة
  • [applicationContext.xml] هو ملف تكوين Spring
  • إلى [2]: العرض [Context.jsp] الذي سيعرض بيانات نطاق التطبيق
  • في [3]:
  • ملف الرسائل [messages.properties]
  • ملف التكوين الرئيسي لـ Struts [struts.xml]. سيتطور مقارنةً بالتطبيقات السابقة.
  • في [4]:
  • إجراء Struts [Action1.java]
  • الفئة [Config.java] التي ستخزن بيانات نطاق التطبيق
  • ملف التكوين الثانوي لـ Struts [example.xml]
  • في [5]: مكتبة Struts 2
  • في [6]:
  • الملفات الضرورية لـ Spring [spring-core, spring-context, spring-beans, spring-web, commons-logging]
  • أرشيف المكون الإضافي لـ Spring الخاص بـ Struts 2. يتيح تكامل Spring مع Struts 2 [struts2-spring-plugin]

بالنسبة للإصدارات السابقة:

  • يجب إضافة ملفات أرشيفية إلى المشروع
  • وتعديل الملفين [web.xml] و [struts.xml]

17.2. Configuration

17.2.1. الملف [web.xml]

يتغير الملف [web.xml] على النحو التالي:


<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <display-name>Exemple 14</display-name>
  <filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>

يتم التعديل في الأسطر 12-14. يتم إضافة ملف listener إلى تطبيق الويب. عند تشغيل تطبيق الويب، سيتم إنشاء مثيل للفئة التي تنفذ ملف listener. وهي فئة تابعة لـ Spring. ستستخدم هذه الفئة الملف [WEB-INF/applicationContext.xml]. وهذا الملف هو كما يلي:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
  
  <!-- تكوين مكونات bean ذات نطاق التطبيق -->
  <bean id="config" class="example.Config" >
    <property name="nbMaxUsers" value="10"/>
  </bean>

</beans>
  • يحتوي ملف تكوين Spring على العلامة الجذرية <beans> (السطران 2 و11). يمكن ترجمة beans إلى «كائنات». سيقوم Spring بإنشاء مثيلات لجميع الكائنات (bean) الموجودة في ملف التكوين هذا. ولا يقوم بذلك إلا مرة واحدة، عند تشغيل Spring عند بدء تشغيل تطبيق الويب.
  • الأسطر 7-9: تُعرِّف حبة (bean) باسم config (المعرّف). ترتبط حبة التكوين هذه بالفئة (class) [example.Config]. وسيقوم Spring بإنشاء مثيل لهذه الفئة.
  • السطر 8: يحدد خاصية للفئة [example.Config]. سيقوم Spring باستدعاء الطريقة [example.Config].setNbMaxUsers(10). لذلك يجب أن تكون الطريقة setNbMaxUsers موجودة في الفئة. وهي كما يلي:

package example;

public class Config {

  private int nbMaxUsers;

  public int getNbMaxUsers() {
    return nbMaxUsers;
  }

  public void setNbMaxUsers(int nbMaxUsers) {
    this.nbMaxUsers = nbMaxUsers;
  }
  
}

لا تُعرّف هذه الفئة سوى حقل nbMaxUsers مع قيمتيه get و set. إذا كنا قد تابعنا ما قيل جيدًا، فعند بدء تشغيل تطبيق الويب، يتم إنشاء مثيل للفئة [Config] بقيمة 10 لحقلها nbMaxUsers. لم نحدد سوى حقل واحد، لكن هذا يكفي لغرض العرض التوضيحي. نريد أن نوضح أن هذا الحقل، الذي قد يمثل الحد الأقصى لعدد المستخدمين، هو بيانات ذات نطاق Application يمكن لجميع الإجراءات الوصول إليها. وهذا ما سنوضحه باستخدام الإجراء [Action1].

17.2.2. الملف [struts.xml]

ملف التكوين الرئيسي لـ Struts هو التالي:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
  <!-- التدويل -->
  <constant name="struts.custom.i18n.resources" value="messages" />
  <!-- تكامل Spring -->
  <constant name="struts.objectFactory.spring.autoWire" value="name" />

  <include file="example/example.xml"/>

  <package name="default" namespace="/" extends="struts-default">
    <default-action-ref name="index" />
    <action name="index">
      <result type="redirectAction">
        <param name="actionName">Action1</param>
        <param name="namespace">/example</param>
      </result>
    </action>
  </package>

</struts>

السطر 10 هو السطر الوحيد الذي يختلف عن الإصدارات السابقة من هذا الملف. وهو يحدد ثابتًا في Struts. يمكن حقن الكائنات التي تم إنشاء مثيلاتها بواسطة Spring في كائنات أخرى. وهذا هو أساس Spring ذاته. هنا، يمكن حقن مرجع إلى الكائن من النوع [Config] الذي أنشأه Spring في كائنات Struts. وهنا يأتي دور المكون الإضافي Spring لـ Struts 2 الذي يضمن تكامل Spring مع Struts.

تشير السطر 10 إلى أن حقن كائنات Spring في كائن Struts سيتم تلقائيًا (autowire) حسب الاسم (value). لنعد إلى ملف التكوين [applicationContext.xml]:


<!-- تكوين حبات نطاق التطبيق -->
  <bean id="config" class="example.Config" >
    <property name="nbMaxUsers" value="10"/>
  </bean>

ما يسميه Struts 2 «اسم البين» هو في الواقع هنا id. لذا، فإن اسم البين من النوع [Config] الذي تم إنشاء مثيل له هو config.

الإجراء [Action1.java] هو التالي:


package example;

import com.opensymphony.xwork2.ActionSupport;
...
public class Action1 extends ActionSupport implements SessionAware, RequestAware, ParameterAware {

  // منشئ بدون معلمات
  public Action1() {
  }
  
  // الجلسة، الطلب، المعلمات
  private Map<String, Object> session;
  private Map<String, Object> request;
  private Map<String, String[]> parameters;
  private Config config;

  @Override
  public String execute() {
  ....
    // الطلب
    request.put("nbMaxUsers", config.getNbMaxUsers());

    // عرض الصفحة JSP
    return SUCCESS;
  }
...
  public Config getConfig() {
    return config;
  }

  public void setConfig(Config config) {
    this.config = config;
  }
}

الإجراء [Action1] مطابق لما كان عليه في التطبيق السابق باستثناء التفاصيل التالية:

  • السطر 15: تمت إضافة حقل config. ونظرًا لأن اسمه يطابق اسم «بين» تم إنشاء مثيل له بواسطة Spring، فسيحصل هذا الحقل تلقائيًا على مرجع إلى هذا الكائن. وبذلك، يمكن للإجراء الوصول إلى تكوين التطبيق أو بشكل أعم إلى البيانات ذات النطاق Application.
  • السطر 31: سيقوم Spring بإنشاء مثيل للحقل config عبر طريقته set. لذا يجب أن تكون هذه الطريقة موجودة.
  • السطر 21: تصل الإجراء إلى معلومات النطاق Application. نضع الحد الأقصى لعدد المستخدمين nbMaxUsers في الاستعلام حتى تتمكن طريقة العرض [Context.jsp] من عرضه.

17.2.3. الملف [example.xml]

ملف التكوين الثانوي لـ Struts مطابق لملف الإصدار السابق:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
  <package name="example" namespace="/example" extends="struts-default">
    <action name="Action1" class="example.Action1">
      <result name="success">/example/Context.jsp</result>
    </action>
  </package>
</struts>

17.3. طريقة العرض [Context.jsp]

الطريقة [Context.jsp] هي كما يلي:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
  <head>
    <title><s:text name="Context.titre"/></title>
    <s:head/>
  </head>

  <body background="<s:url value="/ressources/standard.jpg"/>">
    <h2><s:text name="Context.message"/></h2>
    <h3><s:text name="Context.parameters"/></h3>
    nom : <s:property value="#parameters['nom']"/><br/>
    age : <s:property value="#parameters['age']"/>
    <h3><s:text name="Context.session"/></h3>
    compteur : <s:property value="#session['compteur']"/>
    <h3><s:text name="Context.request"/></h3>
    nbMaxUsers : <s:property value="#request['nbMaxUsers']"/>
  </body>
</html>

17.4. الاختبارات

فيما يلي مثال على التنفيذ:

Image