5. اسپرینگ IoC در عمل
اکنون به چند مثال میپردازیم تا آنچه قبلاً دیدهایم را به کار ببریم.
5.1. مثال ۱
کلاس [Personne.vb] به شرح زیر است:
Namespace istia.st.springioc.demos
Public Class Personne
' fields خصوصی
Private _nom As String
Private _age As Integer
' سازندهٔ پیشفرض
Public Sub New()
End Sub
' ویژگیهای مرتبط با فیلدهای خصوصی
Public Property nom() As String
Get
Return _nom
End Get
Set(ByVal Value As String)
_nom = Value
End Set
End Property
Public Property age() As Integer
Get
Return _age
End Get
Set(ByVal Value As Integer)
_age = Value
End Set
End Property
' رشته هویت
Public Overrides Function tostring() As String
Return String.Format("[{0},{1}]", nom, age)
End Function
' متد init
Public Sub init()
Console.WriteLine("init personne {0}", Me.ToString)
End Sub
' متد بستن
Public Sub close()
Console.WriteLine("destroy personne {0}", Me.ToString)
End Sub
End Class
End Namespace
کلاس شامل:
- دو فیلد خصوصی «name» و «age» که از طریق ویژگیها قابل دسترسی هستند
- یک متد toString برای بازیابی مقدار شی [Personne] به صورت یک رشته
- یک متد
init**که هنگام ایجاد شی توسط Spring فراخوانی میشود، و یک متدclose** که هنگام تخریب شی فراخوانی خواهد شد
برای ایجاد اشیاء از نوع [Personne]، از فایل Spring زیر [spring-config-1.xml] استفاده خواهیم کرد:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<object id="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
</objects>
این فایل
- دو شیء کلیدی به نامهای «person1» و «person2» از نوع [Personne] تعریف میکند
- ویژگیهای [nom, age] را برای هر شخص مقداردهی اولیه میکند
- متدهایی را تعریف میکند که در طول ساخت اولیه شیء [init-method] و در طول تخریب شیء [destroy-method] فراخوانی میشوند
برای آزمایشهایمان از کلاسهای تست NUnit استفاده خواهیم کرد. اولی به شکل زیر خواهد بود:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpringIocDemo1
' شیء تحت تست
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' یک نمونه از فابریک ایجاد میشود
factory = New XmlObjectFactory(New FileStream("spring-config-1.xml", FileMode.Open))
' لاگ
Console.WriteLine("setup test")
End Sub
<Test()> _
Public Sub demo()
'بازیابی اشیاء [Personne] از فایل Spring با استفاده از کلیدهایشان
Dim personne1 As Personne = CType(factory.GetObject("personne1"), Personne)
Console.WriteLine("personne1=" + personne1.ToString())
Dim personne2 As Personne = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
End Sub
<TearDown()> _
Public Sub destroy()
' سینگلتونها نابود میشوند
factory.Dispose()
' منبع کارخانه آزاد میشود
factory = Nothing
'پیگیری
Console.WriteLine("teardown test")
End Sub
End Class
End Namespace
توضیحات:
- برای بازیابی اشیاء تعریفشده در فایل [spring-config-1.xml]، از یک شیء از نوع [XmlObjectFactory] استفاده میکنیم. انواع دیگری از «فکتوری» وجود دارند که امکان دسترسی به اشیاء تکنسخه (singleton) در فایل پیکربندی را فراهم میکنند. شیء [XmlObjectFactory] در متد ویژگی [SetUp] کلاس تست به دست آمده و در یک متغیر خصوصی ذخیره میشود. به یاد داشته باشید که متدی که دارای ویژگی <Setup()> است، قبل از هر تست اجرا میشود.
- فایل [spring-config-1.xml] در پوشه [bin] برنامه قرار داده خواهد شد
- اسپرینگ میتواند از فایلهای پیکربندی در فرمتهای مختلف استفاده کند. شیء [XmlObjectFactory] برای تجزیه یک فایل پیکربندی در فرمت XML استفاده میشود.
- پردازش یک فایل Spring یک شی از نوع [XmlObjectFactory] را بازمیگرداند، که در این مورد شی فابریک است. با استفاده از این شی، میتوان یک singleton با کلید C را از طریق factory.getObject(C) به دست آورد.
- متد [demo] مقادیر اشیاء تکنسخه با کلیدهای «person1» و «person2» را بازیابی و نمایش میدهد.
ساختار پروژه ویژوال استودیو به شرح زیر است:

توضیحات:
- پروژه VS با نام [demo1] نامگذاری شده است
- پوشه [istia] حاوی کد منبع است. کد کامپایلشده در پوشه [bin] که در داخل DLL و [demo1.dll] قرار دارد، قرار خواهد گرفت:

- فایل [spring-config-1.xml] در پوشه [bin] پروژه قرار دارد.
- پوشه [bin] شامل فایلهای مورد نیاز برنامه است:
- Spring.Core.dll، Spring.Core.xml برای کلاسهای Spring
- log4net.dll برای لاگهای Spring
- demo1.dll، که فایل DLL تولیدشده توسط فرآیند تولید پروژه است
فایلهای DLL و [demo1.dll] که توسط پروژه تولید شدهاند، در ابزار تست گرافیکی [Nunit-Gui 2.2] بارگذاری میشوند. این ابزار بهطور خودکار کلاسهای NUnit موجود در فایل DLL را نمایش میدهد:

اجرای متد [demo] در تست NUnit، نتایج نشان داده شده در بالا و بازتولید شده در زیر را به دست میدهد:
توضیحات:
- خط ۲: تست با اجرای متد ویژگی <Setup()> آغاز میشود
- خط ۳: عملیات
ایجاد bean [personne1] را اجباری کرد. از آنجا که تعریف singleton [personne1] شامل مقدار [init-method="init"] بود، متد [init] از شیء ایجادشده [Personne] اجرا شد. پیام مربوطه نمایش داده میشود.
- خط ۴: عملیات
موجب نمایش مقدار شیء ایجادشده [Personne] شده است.
- خطوط ۵–۶: همین اتفاق برای bean کلیدی [personne2] نیز رخ میدهد.
- خط ۷: عملیات نهایی
personne2 = CType(factory.GetObject("personne2"), Personne)
Console.WriteLine("personne2=" + personne2.ToString())
تنها یک خط خروجی تولید کرد:
بنابراین این امر منجر به ایجاد یک شیء جدید از نوع [Personne] نشد. اگر چنین بود، خروجی متد [init] نمایش داده میشد، که در اینجا اینطور نیست. این اصل singleton (تکنسخه) است. به طور پیشفرض، اسپرینگ تنها یک نمونه از بینها را در فایل پیکربندی خود ایجاد میکند. این یک سرویس ارجاع شیء است. اگر برای ارجاع به شیئی که هنوز ایجاد نشده است درخواست شود، آن را ایجاد کرده و ارجاعی به آن بازمیگرداند. اگر شیء قبلاً ایجاد شده باشد، اسپرینگ به سادگی ارجاعی به آن بازمیگرداند.
- خطوط ۸–۱۰: متد ویژگی <TearDown()> اجرا میشود (خط ۱۰). در داخل آن، عملیات
موجب نابودی (destroy) اشیاء تکنسخهای (singletons) ایجادشده توسط Spring میشود. این امر باعث میشود که هر یک از آنها متد [close] خود را اجرا کنند، زیرا در فایل پیکربندی برای هر یک از آنها «destroy-method=close» را مشخص کرده بودیم. این موضوع با خروجی زیر نشان داده میشود:
اکنون که مبانی پیکربندی Spring را پوشش دادهایم، از این پس میتوانیم توضیحات را با سرعت بیشتری پیش ببریم.
5.2. مثال ۲
بیایید کلاس جدید زیر، [Voiture] را در نظر بگیریم:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class Voiture
' fields خصوصی
Private _marque As String
Private _type As String
Private _propriétaire As Personne
' ویژگیهای عمومی
Public Property marque() As String
Get
Return _marque
End Get
Set(ByVal Value As String)
_marque = Value
End Set
End Property
Public Property type() As String
Get
Return _type
End Get
Set(ByVal Value As String)
_type = Value
End Set
End Property
Public Property propriétaire() As Personne
Get
Return _propriétaire
End Get
Set(ByVal Value As Personne)
_propriétaire = Value
End Set
End Property
' سازنده پیشفرض
Public Sub New()
End Sub
' سازندهٔ سهپارامتری
Public Sub New(ByVal marque As String, ByVal type As String, ByVal propriétaire As Personne)
' fields خصوصی از طریق ویژگیهای مرتبط با آنها مقداردهی اولیه میشوند
With Me
.marque = marque
.type = type
.propriétaire = propriétaire
End With
End Sub
' رشته هویتی شیء Car
Public Overrides Function tostring() As String
Return String.Format("[{0},{1},{2}]", marque, type, propriétaire.ToString)
End Function
' متدهای init و destroy
Public Sub init()
Console.WriteLine("init voiture {0}", Me.ToString)
End Sub
Public Sub destroy()
Console.WriteLine("destroy voiture {0}", Me.ToString)
End Sub
End Class
End Namespace
کلاس شامل:
- سه فیلد خصوصی: _type، _marque و _propriétaire. این فیلدها را میتوان از طریق خواص عمومی مقداردهی اولیه و خواند. همچنین میتوان آنها را با استفاده از سازنده Car(String, String, Person) مقداردهی اولیه کرد. این کلاس همچنین دارای یک سازنده بدون آرگومان است که به شما امکان میدهد ابتدا یک شیء [Voiture] را بدون مقدار اولیه ایجاد کرده و سپس آن را از طریق ویژگیهای عمومیاش مقداردهی کنید.
- یک متد toString برای بازیابی مقدار شی [Voiture] به صورت یک رشته
- یک متد
initکه بلافاصله پس از ایجاد شی توسط Spring فراخوانی میشود؛ یک متد **destroy**که هنگام تخریب شی فراخوانی خواهد شد
برای ایجاد اشیاء از نوع [Voiture]، از فایل Spring زیر [spring-config-2.xml] استفاده خواهیم کرد:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- افراد -->
<object id="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
<!-- یک خودرو -->
<object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
destroy-method="destroy">
<constructor-arg index="0">
<value>Peugeot</value>
</constructor-arg>
<constructor-arg index="1">
<value>307</value>
</constructor-arg>
<constructor-arg index="2">
<ref object="personne2"/>
</constructor-arg>
</object>
</objects>
این فایل یک شیء کلیدی به نام "car1" از نوع [Voiture] به تعاریف قبلی اضافه میکند. برای مقداردهی اولیه این شیء میتوانست نوشت:
<object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
destroy-method="destroy">
<constructor-arg index="0">
<value>Peugeot</value>
</constructor-arg>
<constructor-arg index="1">
<value>307</value>
</constructor-arg>
<constructor-arg index="2">
<ref object="personne2"/>
</constructor-arg>
</object>
به جای انتخاب این روش که قبلاً ارائه شده است، در اینجا تصمیم گرفتهایم از سازنده کلاس **Car(String, String, Person)** استفاده کنیم. علاوه بر این، کلاس تکنسخه [voiture1] متدی را برای فراخوانی در هنگام ساخت اولیه شیء [init-method] و متدی را برای فراخوانی در هنگام تخریب شیء [destroy-method] تعریف میکند.
برای تستهای خود، از کلاس تست زیر NUnit [NunitTestSpringIocDemo2] استفاده خواهیم کرد:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpringIocDemo2
' کارخانهٔ تکنفره
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' یک نمونه از کارخانه ایجاد میشود
factory = New XmlObjectFactory(New FileStream("spring-config-2.xml", FileMode.Open))
' لاگ
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' سینگلتونها نابود میشوند
factory.Dispose()
' فابریک آزاد میشود
factory = Nothing
' ردیابی
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo2()
'بازیابی تکنسخه [voiture1]
Dim voiture1 As Voiture = CType(factory.GetObject("voiture1"), Voiture)
Console.WriteLine("Voiture1=[{0}]", voiture1)
End Sub
End Class
End Namespace
توضیحات:
- متدهای ویژگی <Setup()> و <TearDown()> بدون تغییر باقی میمانند.
- متد [demo2] یک مرجع به singleton [voiture1] را بازیابی کرده و آن را نمایش میدهد.
ساختار پروژه Visual Studio همانطور که قبلاً بود باقی میماند. تنها دو کلاس جدید به همراه یک فایل پیکربندی Spring اضافه شدهاند:

اجرای تست NUnit نتایج زیر را تولید میکند:

بیایید درباره خروجیهای صفحه نظر بدهیم:
توضیحات:
- خط ۲: متد attribute [Setup] قبل از هر تست اجرا میشود؛ در اینجا، [demo]
- خط ۳: Spring شروع به ایجاد singleton [voiture1] میکند. این singleton به singleton [personne2] وابسته است که وجود ندارد. بنابراین، مورد دوم ایجاد شده و متد آن [init] اجرا میشود.
- خط ۴: اکنون میتوان singleton [voiture1] را ایجاد کرد. سپس متد آن [init] اجرا میشود.
- خط ۵: متد [demo2] مقدار اشیاء تکنسخه [voiture1] را نمایش میدهد.
- خطوط ۶–۸: متد تست [TearDown] اجرا میشود. اشیاء تکنسخه نابود میشوند که منجر به اجرای متدهایشان [destroy] میگردد
5.3. مثال ۳
ما کلاس جدید زیر را معرفی میکنیم [GroupePersonnes]:
Imports istia.st.springioc.demos
Namespace istia.st.springioc.demos
Public Class GroupePersonnes
' fields خصوصی
Private _membres() As Personne
Private _groupesDeTravail As Hashtable
' ویژگیهای عمومی
Public Property membres() As Personne()
Get
Return _membres
End Get
Set(ByVal Value() As Personne)
_membres = Value
End Set
End Property
Public Property groupesDeTravail() As Hashtable
Get
Return _groupesDeTravail
End Get
Set(ByVal Value As Hashtable)
_groupesDeTravail = Value
End Set
End Property
' سازنده پیشفرض
Public Sub New()
End Sub
'رشته هویت شیء GroupeDePersonnes
Public Overrides Function tostring() As String
' به صورت متناوب از فهرست اعضای گروه عبور میکند
Dim identité As String = "[membres=("
Dim i As Integer
For i = 0 To membres.Length - 2
identité += membres(i).ToString + ","
Next
identité += membres(i).ToString + "), groupes de travail=("
' پیمایش فرهنگ لغت گروه کاری
Dim clés As IEnumerator = groupesDeTravail.Keys.GetEnumerator
Dim clé As Object
While clés.MoveNext
clé = clés.Current
identité += String.Format("[{0},{1}] ", clé, groupesDeTravail(clé))
End While
' بازگرداندن نتیجه
Return identité + "]"
End Function
' متدهای init-destroy
Public Sub init()
Console.WriteLine("init GroupeDePersonnes {0}", Me.ToString)
End Sub
Public Sub destroy()
Console.WriteLine("destroy GroupeDePersonnes {0}", Me.ToString)
End Sub
End Class
End Namespace
دو عضو خصوصی آن عبارتند از:
_membres: آرایهای از افراد که اعضای گروه هستند
_groupesDeTravail: یک دیکشنری که یک شخص را به یک گروه کاری نگاشت میکند
این اعضای خصوصی از طریق ویژگیهای عمومی قابل دسترسی هستند. هدف در اینجا نشان دادن نحوهٔ امکانپذیرسازی راهاندازی اشیاء پیچیده، مانند آنهایی که شامل فیلدهای آرایهای یا دیکشنری هستند، توسط Spring است.
فایل پیکربندی Spring با شناسه [spring-config-3.xml] به شرح زیر خواهد بود:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<!-- از افراد -->
<object id="personne1" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Simon</value>
</property>
<property name="age">
<value>40</value>
</property>
</object>
<object id="personne2" type="istia.st.springioc.demos.Personne, demo1" init-method="init"
destroy-method="close">
<property name="nom">
<value>Brigitte</value>
</property>
<property name="age">
<value>20</value>
</property>
</object>
<!-- یک خودرو -->
<object id="voiture1" type="istia.st.springioc.demos.Voiture, demo1" init-method="init"
destroy-method="destroy">
<constructor-arg index="0">
<value>Peugeot</value>
</constructor-arg>
<constructor-arg index="1">
<value>307</value>
</constructor-arg>
<constructor-arg index="2">
<ref object="personne2" />
</constructor-arg>
</object>
<!-- گروهی از افراد -->
<object id="groupe1" type="istia.st.springioc.demos.GroupePersonnes" init-method="init"
destroy-method="destroy">
<property name="membres">
<list>
<ref object="personne1" />
<ref object="personne2" />
</list>
</property>
<property name="groupesDeTravail">
<dictionary>
<entry key="Brigitte">
<value>Marketing</value>
</entry>
<entry key="Simon">
<value>Ressources humaines</value>
</entry>
</dictionary>
</property>
</object>
</objects>
- برچسب <list> به شما امکان میدهد یک فیلد از نوع آرایه یا از نوع IList را با مقادیر مختلف مقداردهی اولیه کنید.
- برچسب <dictionary> به شما امکان میدهد همین کار را با فیلدی که رابط IDictionary را پیادهسازی میکند انجام دهید
برای آزمایشهایمان، از کلاس تست زیر استفاده خواهیم کرد: NUnit [NunitTestSpringIocDemo3]:
Imports System
Imports Spring.Objects.Factory.Xml
Imports System.IO
Imports NUnit.Framework
Imports istia.st.springioc.demos
Namespace istia.st.springioc.tests
<TestFixture()> _
Public Class NunitTestSpringIocDemo3
' شیء تحت آزمایش
Private factory As XmlObjectFactory
<SetUp()> _
Public Sub init()
' یک نمونه از کارخانه ایجاد میشود
factory = New XmlObjectFactory(New FileStream("spring-config-3.xml", FileMode.Open))
' لاگ
Console.WriteLine("setup test")
End Sub
<TearDown()> _
Public Sub destroy()
' سینگلتونها نابود میشوند
factory.Dispose()
' ردیابی
Console.WriteLine("teardown test")
End Sub
<Test()> _
Public Sub demo3()
'بازیابی Singleton [groupe1]
Dim groupe1 As GroupePersonnes = CType(factory.GetObject("groupe1"), GroupePersonnes)
Console.WriteLine("groupe1={0}", groupe1)
End Sub
End Class
End Namespace
متد [demo3] نمونهٔ واحد [groupe1] را بازیابی کرده و آن را نمایش میدهد.
ساختار پروژهٔ Visual Studio مانند قبل باقی میماند، با این تفاوت که اکنون دو کلاس اضافی و یک فایل پیکربندی دارد.

اجرای متد [demo3] از تست NUnit نتایج زیر را تولید میکند:

توضیحات:
- خط ۲: متد ویژگی [Setup] قبل از هر تست اجرا میشود، در این مورد [demo]
- خطوط ۳–۴: اسپرینگ شروع به ایجاد کلاس تکنفره [groupe1] میکند. این کلاس تکنفره به کلاسهای تکنفره [personne1, personne2] وابسته است که هنوز وجود ندارند. این کلاسها ایجاد شده و متدهایشان [init] اجرا میشوند.
- خط ۵: اکنون میتوان singleton [groupe1] را ایجاد کرد. سپس متد آن [init] اجرا میشود.
- خط ۶: متد [demo3] مقدار سینگلتون [groupe1] را نمایش میدهد.
- خطوط ۷–۱۰: متد [TearDown] تست اجرا میشود. تکنسخهها نابود میشوند که منجر به اجرای متدهای [destroy] آنها میگردد