Skip to content

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

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

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

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. التكوين

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. تتم إضافة مستمع إلى تطبيق الويب. عند تشغيل تطبيق الويب، سيتم إنشاء مثيل للفئة التي تنفذ هذا المستمع. هذه فئة 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">
 
  <!-- application scope bean configuration -->
  <bean id="config" class="example.Config" >
    <property name="nbMaxUsers" value="10"/>
  </bean>
 
</beans>
  • يحتوي ملف تكوين Spring على العلامة الجذرية <beans> (السطران 2 و 11). يمكن اعتبار الفاصوليا ككائنات. سيقوم Spring بإنشاء مثيلات لجميع الكائنات (الفاصوليا) الموجودة في ملف التكوين هذا. ويقوم بذلك مرة واحدة فقط، عند تشغيل مستمع Spring في بداية تطبيق الويب.
  • الأسطر 7-9: تحدد bean باسم config (id). يرتبط bean config بالفئة [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 الخاص بها. لقد حددنا حقلًا واحدًا فقط، ولكن هذا كافٍ لتوضيحنا. نريد أن نوضح أن هذا الحقل، الذي يمكن أن يمثل الحد الأقصى لعدد المستخدمين، هو بيانات على نطاق التطبيق يمكن الوصول إليها من جميع الإجراءات. وهذا ما سنوضحه باستخدام الإجراء [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>
  <!-- internationalization -->
  <constant name="struts.custom.i18n.resources" value="messages" />
  <!-- spring integration -->
  <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) حسب الاسم (القيمة). لنعد إلى ملف التكوين [applicationContext.xml]:


<!-- configuration des beans de portée application -->
  <bean id="config" class="example.Config" >
    <property name="nbMaxUsers" value="10"/>
  </bean>

ما يشير إليه Struts 2 باسم bean هو في الواقع المعرف هنا. لذا في هذه الحالة، يُسمى bean [Config] الذي تم إنشاء مثيل له باسم config.

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


package example;
 
import com.opensymphony.xwork2.ActionSupport;
...
public class Action1 extends ActionSupport implements SessionAware, RequestAware, ParameterAware {
 
  // constructor without parameters
  public Action1() {
  }
  
  // Session, Request, Parametres
  private Map<String, Object> session;
  private Map<String, Object> request;
  private Map<String, String[]> parameters;
  private Config config;
 
  @Override
  public String execute() {
  ....
    // request
    request.put("nbMaxUsers", config.getNbMaxUsers());
 
    // display page JSP
    return SUCCESS;
  }
...
  public Config getConfig() {
    return config;
  }
 
  public void setConfig(Config config) {
    this.config = config;
  }
}

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

  • السطر 15: تمت إضافة حقل تكوين. ونظرًا لأن هذا الحقل يحمل اسم كائن (bean) تم إنشاء مثيل له بواسطة Spring، فسيتلقى هذا الحقل تلقائيًا مرجعًا إلى ذلك الكائن. وبالتالي، فإن الإجراء (action) يمكنه الوصول إلى تكوين التطبيق أو، بشكل أعم، إلى البيانات ذات نطاق التطبيق.
  • السطر 31: سيقوم Spring بإنشاء مثيل لحقل التكوين عبر طريقة set الخاصة به. لذلك يجب أن يكون موجودًا.
  • السطر 21: يصل الإجراء إلى معلومات نطاق التطبيق. نقوم بتضمين العدد الأقصى للمستخدمين، 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