7. الحل
7.1. مشروع Visual Studio
![]() | ![]() |
تعليقات:
- تم وضع الملفات المطلوبة لـ Spring في المجلد [bin]: Spring.Core.dll، log4net.dll، Spring.Core.xml
- كما تم وضع ملفات تكوين Spring [spring-config-3tier-*.xml] في المجلد [bin]
- ستجد تحت الدليل الجذري [istia] الفئات المختلفة للتطبيق
تم تكوين المشروع لإنشاء ملف DLL [spring3tier.dll] في المجلد [bin]:

7.2. الحزمة [istia.st.spring3tier.dao]
واجهة IDao:
Namespace istia.st.spring3tier.dao
Public Interface IDao
' do something in the [dao] layer
Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface
End Namespace
فئة التنفيذ الأولى:
Namespace istia.st.spring3tier.dao
Public Class DaoImpl1
Implements istia.st.spring3tier.dao.IDao
' do something in the diaper [dao]
Public Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDao.doSomethingInDaoLayer
Return a + b
End Function
End Class
End Namespace
- لا تحتوي الفئة على حقول خاصة
- تُرجع الطريقة [doSomethingInDaoLayer] مجموع معلماتها كما هو مطلوب.
فئة تنفيذ ثانية:
Namespace istia.st.spring3tier.dao
Public Class DaoImpl2
Implements istia.st.spring3tier.dao.IDao
' do something in the diaper [dao]
Public Function doSomethingInDaoLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDao.doSomethingInDaoLayer
Return a - b
End Function
End Class
End Namespace
- لا تحتوي الفئة على حقول خاصة
- تُرجع الطريقة [doSomethingInDaoLayer] الفرق بين معلماتها كما هو مطلوب.
7.3. حزمة [istia.st.spring3tier.domain]
واجهة IDomain:
Namespace istia.st.spring3tier.domain
Public Interface IDomain
' do something in the [domain] layer
Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface
End Namespace
فئة التنفيذ الأولى IDomainImpl1:
Imports istia.st.spring3tier.dao
Namespace istia.st.spring3tier.domain
Public Class DomainImpl1
Implements istia.st.spring3tier.domain.IDomain
' private fields
Private _dao As IDao
' associated property
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [domain] layer
Public Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDomain.doSomethingInDomainLayer
a += 1
b += 1
Return _dao.doSomethingInDaoLayer(a, b)
End Function
End Class
End Namespace
- تحتوي الفئة على حقل خاص يمثل مرجعًا إلى عنصر فريد من نوع [IDao]، والذي يوفر الوصول إلى طبقة [Dao]. سيتم تهيئة هذا الحقل بواسطة Spring (حقن التبعية) عند إنشاء الكائن.
- تقوم الطريقة [doSomethingInDomainLayer] بزيادة معلماتها ثم تمررها إلى الطريقة [doSomethingInDaoLayer] للكائن الفردي [dao]
فئة تنفيذ ثانية، IDomainImpl2:
Imports istia.st.spring3tier.dao
Namespace istia.st.spring3tier.domain
Public Class DomainImpl2
Implements istia.st.spring3tier.domain.IDomain
' private fields
Private _dao As IDao
' associated property
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [domain] layer
Public Function doSomethingInDomainLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IDomain.doSomethingInDomainLayer
a -= 1
b -= 1
Return _dao.doSomethingInDaoLayer(a, b)
End Function
End Class
End Namespace
- تحتوي الفئة على حقل خاص يمثل مرجعًا إلى العنصر الفردي [IDao]، الذي يوفر الوصول إلى طبقة [Dao]. سيتم تهيئة هذا الحقل بواسطة Spring (حقن التبعية) عند إنشاء الكائن.
- تقوم طريقة [doSomethingInDomainLayer] بخصم قيم معلماتها ثم تمريرها إلى طريقة [doSomethingInDaoLayer] الخاصة بـ [dao] singleton
7.4. حزمة [istia.st.spring3tier.control]
واجهة IControl:
Namespace istia.st.spring3tier.control
Public Interface IControl
' do something in the [control] layer
Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer
End Interface
End Namespace
فئة التنفيذ الأولى ControlImpl1:
Imports istia.st.spring3tier.domain
Namespace istia.st.spring3tier.control
Public Class ControlImpl1
Implements istia.st.spring3tier.control.IControl
' private fields
Private _domain As IDomain
' associated property
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [control] layer
Public Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IControl.doSomethingInControlLayer
a += 1
b += 1
Return _domain.doSomethingInDomainLayer(a, b)
End Function
End Class
End Namespace
- تحتوي الفئة على حقل خاص يمثل مرجعًا إلى العنصر الفريد من النوع [IDomain]، والذي يوفر الوصول إلى طبقة [Domain]. سيتم تهيئة هذا الحقل بواسطة Spring (حقن التبعية) عند إنشاء الكائن.
- تقوم الطريقة [doSomethingInControlLayer] بزيادة معلماتها ثم تمررها إلى الطريقة [doSomethingInDomainLayer] للكائن الفردي [domain]
فئة تنفيذ ثانية، IControlImpl2:
Imports istia.st.spring3tier.domain
Namespace istia.st.spring3tier.control
Public Class ControlImpl2
Implements istia.st.spring3tier.control.IControl
' private fields
Private _domain As IDomain
' associated property
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' default builder
Public Sub New()
End Sub
' do something in the [control] layer
Public Function doSomethingInControlLayer(ByVal a As Integer, ByVal b As Integer) As Integer Implements IControl.doSomethingInControlLayer
a -= 1
b -= 1
Return _domain.doSomethingInDomainLayer(a, b)
End Function
End Class
End Namespace
- تحتوي الفئة على حقل خاص يمثل مرجعًا إلى العنصر الفريد من النوع [IDomain]، والذي يوفر الوصول إلى طبقة [Domain]. سيتم تهيئة هذا الحقل بواسطة Spring (حقن التبعية) عند إنشاء الكائن.
- تقوم طريقة [doSomethingInControlLayer] بخصم قيم معلماتها ثم تمريرها إلى طريقة [doSomethingInDomainLayer] للكائن الفردي [domain].
7.5. ملفات تكوين [Spring]
يستخدم ملف [spring-config-3tier-1.xml] الإصدار 1 من عمليات التنفيذ:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- the dao class -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- the domain class -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- the control class -->
<object id="control" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain" />
</property>
</object>
</objects>
يستخدم ملف [spring-config-3tier-2.xml] الإصدار 2 من عمليات التنفيذ:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- the dao class -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- the domain class -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- the control class -->
<object id="control" type="istia.st.spring3tier.control.ControlImpl2, spring3tier">
<property name="domain">
<ref object="domain" />
</property>
</object>
</objects>
7.6. حزمة الاختبار [istia.st.spring3tier.tests]
اختبار NUnit [NunitTestSpring3tier.vb]:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.spring3tier.control
Imports istia.st.spring3tier.domain
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpring3tier
' the singleton factories
Private factory1 As XmlObjectFactory
Private factory2 As XmlObjectFactory
<SetUp()> _
Public Sub init()
' singleton factories are created
factory1 = New XmlObjectFactory(New FileStream("spring-config-3tier-1.xml", FileMode.Open))
factory2 = New XmlObjectFactory(New FileStream("spring-config-3tier-2.xml", FileMode.Open))
End Sub
<TearDown()> _
Public Sub destroy()
' we destroy the singletons
factory1.Dispose()
factory2.Dispose()
' we free the singletons factories
factory1 = Nothing
factory2 = Nothing
End Sub
<Test()> _
Public Sub test()
'we retrieve an implementation of the IControl interface
Dim control1 As IControl = CType(factory1.GetObject("control"), IControl)
' we use the
Dim a1 As Integer = 10, b1 As Integer = 20
Dim res1 As Integer = control1.doSomethingInControlLayer(a1, b1)
Assert.AreEqual(34, res1)
' we retrieve another implementation of the IControl interface
Dim control2 As IControl = CType(factory2.GetObject("control"), IControl)
' we use the
Dim a2 As Integer = 10, b2 As Integer = 20
Dim res2 As Integer = control2.doSomethingInControlLayer(a2, b2)
Assert.AreEqual(-10, res2)
End Sub
End Class
End Namespace
يؤدي تشغيل هذا الاختبار إلى النتائج التالية:

سيلاحظ القراء الذين يشاهدون نسخة ملونة من هذا المستند أن النتائج "خضراء"، مما يشير إلى نجاح الاختبار.
7.7. نوع آخر من ملفات تكوين Spring
يمكن تكوين تطبيق VB.NET باستخدام ملف يسمى [App.config]. يتم وضع هذا الملف في جذر مشروع Visual Studio:

يمكن لـ Spring استخدام ملف التكوين هذا. انظر إلى ملف [App.config] التالي، المستوحى من أمثلة من وثائق [Spring.net]:
<?xml version="1.0" encoding="iso-8859-1" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
<resource uri="config://spring/objects" />
</context>
<objects>
<!-- an initial configuration -->
<!-- the dao class -->
<object id="dao1" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- the domain class -->
<object id="domain1" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao1" />
</property>
</object>
<!-- the control class -->
<object id="control1" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain1" />
</property>
</object>
<!-- a second configuration -->
<!-- the dao class -->
<object id="dao2" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- the domain class -->
<object id="domain2" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao2" />
</property>
</object>
<!-- the control class -->
<object id="control2" type="istia.st.spring3tier.control.ControlImpl2, spring3tier">
<property name="domain">
<ref object="domain2" />
</property>
</object>
</objects>
</spring>
</configuration>
ملاحظة: المعلومات الواردة أدناه مقدمة مع بعض التحفظات. لست متأكدًا من أنني فهمت بشكل صحيح معنى جميع العناصر الموجودة في ملف التكوين أعلاه.
تتطلب صيغة XML لملف [App.config] أن يتبع هذا التنسيق:
يمكن تفويض إدارة الأقسام المختلفة في [App.config] إلى برامج خارجية. وهذا ما يتم هنا في قسم [ConfigSections]:
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
يعني الكود أعلاه أن القسم المسمى [spring/context] يجب أن تداره الفئة [Spring.Context.Support.ContextHandler] الموجودة في تجميع [Spring.Core.dll]، وأن القسم المسمى [spring/objects] يجب أن تعالجه الفئة [Spring.Context.Support.DefaultSectionHandler]، الموجودة أيضًا في تجميع [Spring.Core.dll].
القسم [spring/context] هو كما يلي:
<spring>
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
<resource uri="config://spring/objects" />
</context>
...
</spring>
يبدو أن هذا يشير إلى أن قسم [spring/objects] من ملف التكوين يجب أن تتم إدارته بواسطة فئة [Spring.Context.Support.XmlApplicationHandler]، والتي يمكن العثور عليها في تجميع [Spring.Core.dll]. يجب أن تستخدم هذه الفئة مورد XML موجود في [config://spring/objects]، أي قسم [spring/objects] من ملف التكوين الحالي.
في قسم [spring/objects]، نجد صيغة Spring التي أصبحنا الآن على دراية بها.
في قسم <objects>... </objects> من ملف [App.config]، قمنا بتعريف تكوينين محتملين لتطبيقنا ثلاثي المستويات:
- أحدهما يستخدم الإصدار 1 من تطبيقات الواجهة
- والآخر يستخدم الإصدار 2 من نفس عمليات التنفيذ تلك
الآن، كيف تستخدم ملف [App.config]؟
يُظهر الكود التالي تطبيق وحدة التحكم الذي يستخدم ملف [App.config] السابق:
Imports System
Imports Spring.Context
Imports System.IO
Imports NUnit.Framework
Imports istia.st.spring3tier.control
Imports istia.st.spring3tier.domain
Imports System.Configuration
Namespace istia.st.springioc.tests
Module MainTestSpring3tier
Public Sub main()
' the Spring context that will allow us to retrieve singletons
Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
' we retrieve a 1st implementation of the IControl interface
Dim control1 As IControl = CType(contexte.GetObject("control1"), IControl)
' we use the
Dim a1 As Integer = 10, b1 As Integer = 20
Console.WriteLine("res1({0},{1})={2}", a1, b1, control1.doSomethingInControlLayer(a1, b1))
' we retrieve another implementation of the IControl interface
Dim control2 As IControl = CType(contexte.GetObject("control2"), IControl)
' we use the
Dim a2 As Integer = 10, b2 As Integer = 20
Console.WriteLine("res2({0},{1})={2}", a2, b2, control2.doSomethingInControlLayer(a2, b2))
' break
Console.WriteLine("Tapez [entrée] pour continuer...")
Console.ReadLine()
End Sub
End Module
End Namespace
تعليقات:
- في أمثلة NUnit التي استخدمناها حتى الآن، استخدمنا كائن [XmlObjectFactory] للحصول على الكائنات الفردية التي نحتاجها. هنا، الكائن المستخدم هو من النوع [IApplicationContext]، وهي واجهة Spring. يتم الحصول عليه من [App.config] باستخدام فئة [ConfigurationSettings]، وهي فئة تُستخدم تقليديًا في .NET لمعالجة ملفات التكوين.
- نطلب المعالج الخاص بقسم [spring/context]. إذا رجعنا إلى ملف [App.config]، نرى أن هذا المعالج من نوع [XmlApplicationContext] وهو مسؤول عن إدارة قسم [spring/objects] في [App.config].
- بمجرد استرداد كائن [IApplicationContext]، يتم استخدامه تمامًا مثل كائن [XmlObjectFactory] الذي كنا نستخدمه حتى الآن.
البرنامج السابق يسمى [MainTestSpring3tier.vb] ويقع في حزمة [tests]:

تم تكوين مشروع [spring3tier] بحيث [MainTestSpring3tier]:

يؤدي تشغيل المشروع إلى النتائج التالية:
7.8. الخلاصة
يوفر إطار عمل Spring مرونة حقيقية في كل من بنية التطبيق وتكوينه. استخدمنا مفهوم IoC، وهو أحد ركيزتي Spring. الركيزة الأخرى هي AOP (البرمجة الموجهة نحو الجوانب)، والتي لم نتطرق إليها. تتيح لك إضافة "سلوك" إلى طريقة فئة عبر التكوين دون تعديل كود الطريقة. بعبارات بسيطة، تتيح لك AOP تصفية المكالمات إلى طرق معينة:
![]() |
- يمكن تنفيذ المرشح قبل أو بعد الطريقة المستهدفة M، أو كليهما.
- لا تعرف الطريقة M بوجود هذه المرشحات. يتم تعريفها في ملف تكوين Spring.
- لا يتم تعديل كود الطريقة M. المرشحات هي فئات Java يجب تنفيذها. يوفر Spring مرشحات محددة مسبقًا، خاصة لإدارة معاملات أنظمة إدارة قواعد البيانات (DBMS).
- المرشحات هي حبوب (beans)، وبالتالي يتم تعريفها في ملف تكوين Spring كحبوب.
أحد المرشحات الشائعة هو مرشح المعاملات. لنفترض أن هناك طريقة M في طبقة الأعمال تقوم بعملية لا تنفصل عن البيانات (وحدة عمل). وهي تستدعي طريقتين في طبقة DAO، M1 و M2، لتنفيذ هاتين العمليتين.
![]() |
نظرًا لوجودها في طبقة الأعمال، فإن الطريقة M تستبعد تخزين البيانات الأساسي. فهي لا تحتاج، على سبيل المثال، إلى افتراض أن البيانات موجودة في نظام إدارة قواعد البيانات (DBMS) وأنها تحتاج إلى وضع الاستدعاءين للطريقتين M1 و M2 ضمن معاملة نظام إدارة قواعد البيانات. الأمر متروك لطبقة DAO للتعامل مع هذه التفاصيل. أحد الحلول للمشكلة السابقة هو إنشاء طريقة في طبقة DAO تقوم هي نفسها باستدعاء الطريقتين M1 و M2، مع تغليف هذه الاستدعاءات ضمن معاملة نظام إدارة قواعد البيانات (DBMS).
![]() |
يعد حل التصفية AOP أكثر مرونة. فهو يتيح لك تعريف مرشح يقوم، قبل استدعاء M، ببدء معاملة، وبعد الاستدعاء، يقوم بإجراء التثبيت أو التراجع حسب الاقتضاء.
![]() |
هناك عدة مزايا لهذا النهج:
- بمجرد تعريف المرشح، يمكن تطبيقه على طرق متعددة، على سبيل المثال، جميع الطرق التي تتطلب معاملة
- لا تحتاج الطرق التي تمت تصفيتها إلى إعادة كتابة
- نظرًا لأن المرشحات التي سيتم استخدامها يتم تحديدها من خلال التكوين، فيمكن تغييرها
لمزيد من المعلومات: http://www.springframework.net.





