7. یک راهحل
7.1. پروژه ویژوال استودیو
![]() | ![]() |
توضیحات:
- فایلهای مورد نیاز 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
' کاری در لایه [dao] انجام دهید
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
' کاری در لایه [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
' کاری در لایه [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
' کاری در لایه [domain] انجام دهید
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
' fields خصوصی
Private _dao As IDao
' مالکیت مرتبط
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' سازندهٔ پیشفرض
Public Sub New()
End Sub
' انجام کاری در لایه [domain]
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
- این کلاس یک فیلد خصوصی دارد که ارجاعی به نماد واحد (singleton) از نوع [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
' fields خصوصی
Private _dao As IDao
' ویژگی مرتبط
Public WriteOnly Property dao() As IDao
Set(ByVal Value As IDao)
_dao = Value
End Set
End Property
' سازنده پیشفرض
Public Sub New()
End Sub
' کاری در لایه [domain] انجام دهید
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] از نوع singleton [dao] ارسال میکند
7.4. پکیج [istia.st.spring3tier.control]
رابط IControl:
Namespace istia.st.spring3tier.control
Public Interface IControl
' انجام کاری در لایه [control]
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
' fields خصوصی
Private _domain As IDomain
' مالکیت مرتبط
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' سازندهٔ پیشفرض
Public Sub New()
End Sub
' انجام کاری در لایه [control]
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
- این کلاس یک فیلد خصوصی دارد که ارجاعی به نماد واحد (singleton) از نوع [IDomain] است، که دسترسی به لایه [Domain] را فراهم میکند. این فیلد توسط Spring (تزریق وابستگی) هنگام ساخت شیء مقداردهی اولیه خواهد شد.
- متد [doSomethingInControlLayer] پارامترهای خود را افزایش داده و سپس آنها را به متد [doSomethingInDomainLayer] از نوع singleton [domain] ارسال میکند
یک کلاس پیادهسازی دوم، IControlImpl2:
Imports istia.st.spring3tier.domain
Namespace istia.st.spring3tier.control
Public Class ControlImpl2
Implements istia.st.spring3tier.control.IControl
' fields خصوصی
Private _domain As IDomain
' ویژگی مرتبط
Public WriteOnly Property domain() As IDomain
Set(ByVal Value As IDomain)
_domain = Value
End Set
End Property
' سازنده پیشفرض
Public Sub New()
End Sub
' انجام کاری در لایه [control]
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
- این کلاس یک فیلد خصوصی دارد که ارجاعی به نماد واحد (singleton) از نوع [IDomain] است، که دسترسی به لایه [Domain] را فراهم میکند. این فیلد توسط Spring (تزریق وابستگی) هنگام ساخت شیء مقداردهی اولیه خواهد شد.
- متد [doSomethingInControlLayer] پارامترهای خود را کاهش داده و سپس آنها را به متد [doSomethingInDomainLayer] از نوع singleton [domain] ارسال میکند.
7.5. فایلهای پیکربندی [Spring]
فایل [spring-config-3tier-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>
<!-- کلاس DAO -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!-- کلاس دامنه -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!-- کلاس کنترل -->
<object id="control" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain" />
</property>
</object>
</objects>
فایل [spring-config-3tier-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>
<!--کلاس DAO -->
<object id="dao" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- کلاس دامنه -->
<object id="domain" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao" />
</property>
</object>
<!--کلاس کنترل -->
<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
'کارخانههای تکنسخه
Private factory1 As XmlObjectFactory
Private factory2 As XmlObjectFactory
<SetUp()> _
Public Sub init()
'ایجاد کارخانههای تکنمونهای
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()
' از بین بردن سینگلتونها
factory1.Dispose()
factory2.Dispose()
'آزادسازی کارخانههای تکنسخه
factory1 = Nothing
factory2 = Nothing
End Sub
<Test()> _
Public Sub test()
'بازیابی یک پیادهسازی از رابط IControl
Dim control1 As IControl = CType(factory1.GetObject("control"), IControl)
' استفاده از کلاس
Dim a1 As Integer = 10, b1 As Integer = 20
Dim res1 As Integer = control1.doSomethingInControlLayer(a1, b1)
Assert.AreEqual(34, res1)
'بازیابی یک پیادهسازی دیگر از رابط IControl
Dim control2 As IControl = CType(factory2.GetObject("control"), IControl)
'ما از کلاس استفاده میکنیم
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 قرار میگیرد:

اسپرینگ میتواند از این فایل پیکربندی استفاده کند. بیایید فایل زیر را در نظر بگیریم، [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>
<!--یک پیکربندی اولیه -->
<!--کلاس DAO -->
<object id="dao1" type="istia.st.spring3tier.dao.DaoImpl1, spring3tier"></object>
<!--کلاس دامنه -->
<object id="domain1" type="istia.st.spring3tier.domain.DomainImpl1, spring3tier">
<property name="dao">
<ref object="dao1" />
</property>
</object>
<!--کلاس کنترل -->
<object id="control1" type="istia.st.spring3tier.control.ControlImpl1, spring3tier">
<property name="domain">
<ref object="domain1" />
</property>
</object>
<!--یک پیکربندی دوم -->
<!--کلاس DAO -->
<object id="dao2" type="istia.st.spring3tier.dao.DaoImpl2, spring3tier"></object>
<!-- کلاس دامنه -->
<object id="domain2" type="istia.st.spring3tier.domain.DomainImpl2, spring3tier">
<property name="dao">
<ref object="dao2" />
</property>
</object>
<!--کلاس کنترل -->
<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] و c.a.d از فایل پیکربندی جاری است.
بخش [spring/objects] شامل سینتکس Spring است که اکنون با آن آشنا هستیم.
در بخش <objects>... </objects> فایل [App.config]، ما دو پیکربندی ممکن را برای برنامهٔ سهلایهٔ خود تعریف کردهایم:
- یکی که از نسخهٔ ۱ پیادهسازیهای رابط استفاده میکند
- و دیگری که از نسخهٔ ۲ همین پیادهسازیها استفاده میکند
حال، چگونه از فایل [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()
' کانتکست Spring که به ما امکان بازیابی singletonها را میدهد
Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
' ما یک پیادهسازی اول از اینفریس را بازیابی میکنیم IControl
Dim control1 As IControl = CType(contexte.GetObject("control1"), IControl)
' ما از کلاس استفاده میکنیم
Dim a1 As Integer = 10, b1 As Integer = 20
Console.WriteLine("res1({0},{1})={2}", a1, b1, control1.doSomethingInControlLayer(a1, b1))
' ما پیادهسازی دیگری از رابط IControl را بازیابی میکنیم
Dim control2 As IControl = CType(contexte.GetObject("control2"), IControl)
' ما از کلاس استفاده میکنیم
Dim a2 As Integer = 10, b2 As Integer = 20
Console.WriteLine("res2({0},{1})={2}", a2, b2, control2.doSomethingInControlLayer(a2, b2))
'مکث
Console.WriteLine("Tapez [entrée] pour continuer...")
Console.ReadLine()
End Sub
End Module
End Namespace
توضیحات:
- در مثالهای NUnit که تاکنون استفاده کردهایم، از یک شیء [XmlObjectFactory] برای بهدستآوردن singletonهای مورد نیازمان استفاده کردیم. در اینجا، شیء مورد استفاده از نوع [IApplicationContext] است، که یک interface در Spring است. این از طریق کلاس [ConfigurationSettings] که کلاسی است که بهطور سنتی در .NET برای پردازش فایلهای پیکربندی استفاده میشود، از [App.config] بهدست میآید.
- ما هَندلرِ بخش [spring/context] را فراخوانی میکنیم. اگر به فایل [App.config] مراجعه کنیم، میبینیم که از نوع [XmlApplicationContext] است و مسئول مدیریت بخش [spring/objects] از [App.config] میباشد.
- پس از بازیابی شیء از نوع [IApplicationContext]، از آن به همان شیوهای استفاده میشود که تا کنون از شیء [XmlObjectFactory] استفاده کردهایم.
برنامه قبلی با نام [MainTestSpring3tier.vb] و در بسته [tests] قرار دارد:

پروژه [spring3tier] طوری پیکربندی شده است که [MainTestSpring3tier]:

اجرای پروژه نتایج زیر را تولید میکند:
7.8. Conclusion
چارچوب Spring انعطافپذیری واقعی را هم در معماری برنامه و هم در پیکربندی ارائه میدهد. ما از مفهوم IoC، یکی از دو ستون Spring، استفاده کردهایم. ستون دیگر AOP (برنامهنویسی جنبهگرا) است که به آن نپرداختهایم. این امکان را میدهد که «رفتار» به یک متد کلاس از طریق پیکربندی اضافه شود بدون آنکه کد متد تغییر یابد. به طور کلی، AOP امکان فیلتر کردن فراخوانیهای برخی متدها را فراهم میکند:
![]() |
- فیلتر میتواند قبل یا بعد از متد هدف M، یا هر دو اجرا شود.
- متد M از وجود این فیلترها بیخبر است. آنها در فایل پیکربندی Spring تعریف میشوند.
- کد متد M تغییر نمیکند. فیلترها کلاسهای Java هستند که باید پیادهسازی شوند. Spring فیلترهای از پیش تعریفشدهای را ارائه میدهد، بهویژه برای مدیریت تراکنشها در SGBD.
- فیلترها بیین هستند و به همین دلیل در فایل پیکربندی Spring بهعنوان بیین تعریف میشوند.
یک فیلتر رایج، فیلتر تراکنشی است. بیایید متد M را در لایه کسبوکار در نظر بگیریم که دو عملیات جداییناپذیر بر روی دادهها (یک واحد کار) انجام میدهد. این متد برای انجام این دو عملیات، دو متد M1 و M2 را از لایه DAO فراخوانی میکند.
![]() |
از آنجا که در لایه کسبوکار قرار دارد، متد M رسانه ذخیرهسازی داده را انتزاع میکند. برای مثال، فرض میکند که دادهها در یک SGBD هستند و باید دو فراخوانی متدهای M1 و M2 را در یک تراکنش SGBD قرار دهد. بر عهده لایه DAO است که این جزئیات را مدیریت کند. بنابراین، یک راهحل برای مشکل قبلی این است که متدی در لایه DAO ایجاد شود که خود متدهای M1 و M2 را فراخوانی کند، فراخوانیهایی که آن را در یک تراکنش SGBD محصور میکند.
![]() |
راه حل فیلترینگ AOP انعطافپذیرتر است. این امکان را به شما میدهد که فیلتری را تعریف کنید که قبل از فراخوانی M، یک تراکنش را آغاز میکند و پس از فراخوانی، بسته به شرایط، یک commit یا rollback انجام میدهد.
![]() |
این رویکرد مزایای متعددی دارد:
- پس از تعریف فیلتر، میتوان آن را به چندین متد اعمال کرد، برای مثال به همه متدهایی که به یک تراکنش نیاز دارند.
- روشهایی که به این صورت فیلتر میشوند نیازی به بازنویسی ندارند
- از آنجایی که فیلترهای مورد استفاده از طریق پیکربندی تعریف میشوند، میتوان آنها را تغییر داد
برای اطلاعات بیشتر: http://www.springframework.net.





