Skip to content

7. یک راه‌حل

7.1. پروژه ویژوال استودیو

توضیحات:

  • فایل‌های مورد نیاز Spring در پوشه [bin] قرار داده شده‌اند: Spring.Core.dll, log4net.dll, Spring.Core.xml
  • فایل‌های پیکربندی Spring [spring-config-3tier-*.xml] نیز در پوشه [bin] قرار داده شده‌اند
  • در زیر دایرکتوری ریشه [istia]، کلاس‌های مختلف برنامه را خواهید یافت

پروژه برای تولید DLL و [spring3tier.dll] در پوشه [bin] پیکربندی شده است:

Image

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

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

Image

خوانندگانی که نسخهٔ رنگی این سند را مشاهده می‌کنند، خواهند دید که نتایج «سبز» هستند، که نشان‌دهندهٔ موفقیت آزمون است.

7.7. نوع دیگری از فایل پیکربندی Spring

یک برنامه VB.net را می‌توان با استفاده از فایلی به نام [App.config] پیکربندی کرد. این فایل در ریشه پروژه Visual Studio قرار می‌گیرد:

Image

اسپرینگ می‌تواند از این فایل پیکربندی استفاده کند. بیایید فایل زیر را در نظر بگیریم، [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] ایجاب می‌کند که با سینتکس زیر مطابقت داشته باشد:

<configuration>
....
</configuration>

پردازش بخش‌های مختلف [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] قرار دارد:

Image

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

Image

اجرای پروژه نتایج زیر را تولید می‌کند:

res1(10,20)=34
res2(10,20)=-10
Tapez [entrée] pour continuer...

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.