Skip to content

2. پی‌کنفیگور کردن یک برنامه با اسپرینگ

بیایید یک برنامه کلاسیک سه‌لایه را در نظر بگیریم:

ما فرض می‌کنیم که دسترسی به لایه DAO توسط یک رابط [IArticlesDao] کنترل می‌شود:

....
Namespace istia.st.articles.dao

    Public Interface IArticlesDao
         'فهرست تمام آیتم‌ها
        Function getAllArticles() As IList
         'یک آیتم اضافه می‌کند
        Function ajouteArticle(ByVal unArticle As Article) As Integer
         'یک آیتم را حذف می‌کند
        Function supprimeArticle(ByVal idArticle As Integer) As Integer
         ' یک آیتم را ویرایش می‌کند
        Function modifieArticle(ByVal unArticle As Article) As Integer
         'جستجوی یک آیتم
        Function getArticleById(ByVal idArticle As Integer) As Article
         ' تمام آیتم‌ها را حذف می‌کند
        Sub clearAllArticles()
         ' اضافه کردن اقلام به یک تراکنش
        Sub doInsertionsInTransaction(ByVal articles As Article())
         'موجودی یک کالا را تغییر می‌دهد
        Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
    End Interface
End Namespace

در لایه دسترسی به داده‌ها، یا لایه DAO (Data Access Object)، معمولاً با یک SGBD کار می‌شود. بیایید موردی را در نظر بگیریم که از طریق یک درایور ODBC به آن دسترسی پیدا می‌شود. اسکلت یک کلاس که به این منبع ODBC دسترسی دارد می‌تواند به شکل زیر باشد:

NameSpace istia.st.articles.dao

Imports System.Data.Odbc
...
    Public Class ArticlesDaoPlainODBC
        Implements istia.st.articles.dao.IArticlesDao

         ': فیلدهای خصوصی
        Private connexion As OdbcConnection = Nothing
        Private DSN As String

        Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)
             'نام منبع را بازیابی می‌کند ODBC
            Me.DSN = DSN
             ' رشته اتصال را ایجاد می‌کند
            Dim connectString As String = String.Format("DSN={0};UID={1};PWD={2}", DSN, user, passwd)
             'اتصال را ایجاد می‌کند
            connexion = New OdbcConnection(connectString)
        End Sub

....
    End Class
End NameSpace

برای انجام عملیاتی روی منبع ODBC، هر روشی به یک شیء [OdbcConnection] نیاز دارد که نمایانگر اتصال به پایگاه داده است و از طریق آن داده‌ها بین پایگاه داده و برنامه مبادله می‌شوند. برای ایجاد این شیء، سه مورد اطلاعات مورد نیاز است:

DSN As String
نام منبع ODBC
user As String
هویتی که اتصال تحت آن ایجاد می‌شود
passwd As String
رمز عبور مرتبط با این شناسه

کلاس ما [ArticlesDaoPlainODBC] این اطلاعات را از طریق عامل خارجی که یک نمونه از کلاس را ایجاد می‌کند، به‌دست می‌آورد. ممکن است این سؤال پیش بیاید که عامل چگونه سه اطلاعات مورد نیاز برای نمونه‌سازی کلاس [ArticlesDaoPlainODBC] را به‌دست می‌آورد. بیایید یک مثال بزنیم. فرض کنید می‌خواهیم یک کلاس تست برای لایه [Dao] بنویسیم. معماری زیر را خواهیم داشت:

اسکلت یک کلاس تست Nunit با نام [http://www.nunit.org/] ممکن است به این شکل باشد:

Imports System
Imports System.Collections
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports ArticlesDaoSqlmap = istia.st.articles.dao.ArticlesDaoSqlMap
Imports Article = istia.st.articles.domain.Article
Imports System.Threading

    <TestFixture()> _
    Public Class NunitTestArticlesDaoPlainOdbc

         ' شیء تحت آزمون
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
             'یک نمونه از شیء تحت آزمون ایجاد می‌شود
            articlesDao = New ArticlesDaoPlainODBC("odbc-firebird-articles", "SYSDBA", "masterkey")
        End Sub

        <Test()> _
        Public Sub testGetAllArticles()
             ' بررسی بصری
            listArticles()
        End Sub

         'خروجی صفحه
        Private Sub listArticles()
            Dim articles As IList = articlesDao.getAllArticles
            For i As Integer = 0 To articles.Count - 1
                Console.WriteLine(CType(articles(i), Article).ToString)
            Next
        End Sub


    End Class

محیط تست Nunit یک پورت به پلتفرم NET از محیط JUnit است که برای پلتفرم جاوا وجود دارد. در کلاس بالا، متدی که دارای ویژگی <SetUp()> است، قبل از هر متد تست اجرا می‌شود. متدی که دارای ویژگی <TearDown()> است، بعد از هر تست اجرا می‌شود. در مثال بالا هیچ‌کدام وجود ندارند. در اینجا، می‌بینیم که متد [init] که دارای ویژگی <SetUp()> است، با هاردکد کردن سه اطلاعات مورد نیاز برای سازنده (constructor) آن شیء، یک شیء [ArticlesDaoPlainODBC] را نمونه‌سازی می‌کند.

کلاس تست ما در برابر هر تغییری در یکی از مقادیر سخت‌کدشده آسیب‌پذیر است. ترجیح داده می‌شود که این مقادیر در یک فایل پیکربندی ذخیره شوند تا هنگام تغییر، از کامپایل مجددهای غیرضروری جلوگیری شود. رویکرد استاندارد برای پیکربندی یک برنامه، استفاده از فایلی است که حاوی تمام اطلاعاتی باشد که احتمالاً در طول زمان تغییر می‌کنند. انواع مختلفی از فایل‌های پیکربندی وجود دارد. روند کنونی استفاده از فایل‌های XML است. این رویکردی است که Spring اتخاذ کرده است. فایلی که یک شیء [ArticlesDaoPlainODBC] را پیکربندی می‌کند ممکن است به این شکل باشد:

<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">

<objects>
     <!--کلاسی که رابط IArticlesDao را پیاده‌سازی می‌کند -->
    <description>Gestion d'une table d'articles</description>
    <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
      <constructor-arg index="0">
            <value>odbc-firebird-articles</value>
        </constructor-arg>
         <constructor-arg index="1">
            <value>SYSDBA</value>
        </constructor-arg>
         <constructor-arg index="2">
            <value>masterkey</value>
        </constructor-arg>
    </object>
</objects>

فایل پیکربندی Spring اشیایی را که باید نمونه شوند توصیف می‌کند. به طور کلی، مشخص نمی‌کند که چه زمانی این اشیاء نمونه خواهند شد. بنابراین زمان نمونه‌سازی آن‌ها توسط کدی که از این فایل استفاده می‌کند تعیین می‌شود. اشیایی که در فایل پیکربندی Spring توصیف شده‌اند می‌توانند به دو روش مختلف نمونه و اولیه شوند:

  • با مشخص کردن، همان‌طور که در بالا ذکر شد، پارامترهایی که باید به سازندهٔ شیء ارسال شوند
  • با ارائه مقادیر برای ویژگی‌های شیء (Property). در این حالت، شیء باید دارای یک سازنده پیش‌فرض باشد که اسپرینگ از آن برای نمونه‌سازی استفاده خواهد کرد.

اشیاء در یک برنامه کاربردی که نقششان ارائه یک سرویس است، اغلب به صورت یک نمونه واحد ایجاد می‌شوند. این‌ها به عنوان «سینگلتون‌ها» (singletons) شناخته می‌شوند. بنابراین، در مثال ما از یک برنامه کاربردی چندلایه که در ابتدای این سند ارائه شد، دسترسی به پایگاه داده محصولات توسط یک نمونه واحد از کلاس [ArticlesDaoPlainODBC] مدیریت خواهد شد. در یک برنامه وب، این اشیاء سرویس همزمان به چندین مشتری خدمت می‌کنند. برای هر مشتری یک شیء سرویس ایجاد نمی‌شود.

فایل پیکربندی Spring فوق اجازه می‌دهد تا یک شیء سرویس از نوع [ArticlesDaoPlainODBC] در بسته‌ای به نام [istia.st.articles.dao] ایجاد شود. سه اطلاعات مورد نیاز برای سازنده این شیء در داخل یک تگ <object>...</object> تعریف شده‌اند. به ازای هر تعداد singleton که باید ایجاد شوند، به همان تعداد تگ <object> وجود خواهد داشت.

بیایید پیکربندی را با جزئیات بیشتری بررسی کنیم:

<objects>
...
</objects>

<objects> تگ ریشه یک فایل پیکربندی Spring است. این تگ، توصیف اشیاء تک‌نمونه (singleton) را که باید نمونه‌سازی شوند، اعلام می‌کند.

    <description>Gestion d'une table d'articles</description>

تگ <description> اختیاری است. برای مثال می‌توان از آن برای توصیف هدف فایل پیکربندی استفاده کرد.

<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
...    
</object>

برچسب <object> برای توصیف شیئی که باید نمونه برداری شود استفاده می‌شود. این برچسب در اینجا دو ویژگی دارد:

  • name: شناسه‌ی شیء. کد خارجی با استفاده از این نام به شیء ارجاع خواهد داد.
  • class: به شکل «نام کلاس، نام مونتاژ». بخش اول نام کامل کلاسی است که باید نمونه‌سازی شود. بخش دوم نام DLL حاوی این کلاس است. در مثال ما، کلاس در فایلی به نام [articlesdao.dll] قرار دارد.

محتوای تگ <object> برای توصیف نحوهٔ نمونه‌سازی شیء استفاده می‌شود:

    <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
      <constructor-arg index="0">
            <value>odbc-firebird-articles</value>
        </constructor-arg>
         <constructor-arg index="1">
            <value>SYSDBA</value>
        </constructor-arg>
         <constructor-arg index="2">
            <value>masterkey</value>
        </constructor-arg>
    </object>

بیایید امضای سازنده را برای کلاس [ArticlesDaoPlainODBC] به یاد آوریم:

        Public Sub New(ByVal DSN As String, ByVal user As String, ByVal passwd As String)

شیء Spring با شناسه [articlesdao] توسط کانتراکتور فوق با استفاده از سه مورد اطلاعات از فایل پیکربندی: [odbc-firebird-articles, SYSDBA, masterkey]، نمونه سازی خواهد شد.

ابژه‌های تعریف‌شده در فایل Spring در چه زمانی نمونه‌سازی می‌شوند؟ در هر برنامه‌ای، یک متد وجود دارد که تضمین می‌شود اولین متدی باشد که اجرا می‌شود. معمولاً در همین متد است که نمونه‌سازی singletonها درخواست می‌شود. ابتدایی‌سازی یک برنامه ممکن است به متد main** همان برنامه، در صورت وجود، واگذار شود. برای برنامه‌ای به نام ASP.NET، این ممکن است متد [Application_Start] در فایل [global.asax] باشد. برای کلاس آزمایشی ما [Nunit]، برنامه در متدی که با ویژگی <Setup()>** مرتبط است، راه‌اندازی می‌شود.

چگونه از فایل پیکربندی بالا در کلاس [Nunit] خود استفاده کنیم؟ در اینجا یک مثال آورده شده است:

Imports System
Imports System.Collections
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports ArticlesDaoSqlmap = istia.st.articles.dao.ArticlesDaoSqlMap
Imports Article = istia.st.articles.domain.Article
Imports System.Threading
Imports Spring.Objects.Factory.Xml
Imports System.IO

    <TestFixture()> _
    Public Class NunitSpringTestArticlesDaoPlainOdbc

         ' شیء تحت آزمون
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
       'یک نمونه از سازندهٔ شیء Spring بازیابی می‌شود
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-plainodbc.xml", FileMode.Open))
       'درخواست ایجاد نمونهٔ شی DAO «articles»
            articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
        End Sub

        <Test()> _
        Public Sub testGetAllArticles()
             ' تأیید بصری
            listArticles()
        End Sub

         'خروجی صفحه
        Private Sub listArticles()
            Dim articles As IList = articlesDao.getAllArticles
            For i As Integer = 0 To articles.Count - 1
                Console.WriteLine(CType(articles(i), Article).ToString)
            Next
        End Sub
    End Class

توضیحات:

  • برای نمونه‌سازی اشیاء از فایل پیکربندی Spring، از یک شیء از نوع [XmlObjectFactory] استفاده می‌کنیم. این یک شیء «کارخانه» (Factory) است، c.a.d – شیء‌ای که برای ایجاد اشیاء دیگر استفاده می‌شود (Factory = کارخانه، کارگاه). اسپرینگ انواع مختلفی از «فکتوری» دارد که بسته به فایل پیکربندی مورد استفاده متفاوت است. در اینجا، فایل یک فایل XML است، بنابراین از نوع [XmlObjectFactory] استفاده می‌کنیم.
  • به طور منطقی، یک شیء از نوع [XmlObjectFactory] به نام فایل پیکربندی XML نیاز دارد، که در این مورد [spring-config-plainodbc.xml] است. به طور دقیق، نوع [XmlObjectFactory] با یک جریان خوانش ایجاد شده از فایل XML که نام آن مشخص شده است، نمونه سازی می‌شود.
  • پس از ایجاد شیء از نوع [XmlObjectFactory]، یک شیء از فایل پیکربندی از طریق [XmlObjectFactory] به دست می‌آید.getObject("identifier") که در آن "identifier" ویژگی [id] یکی از اشیاء در فایل پیکربندی است.
  • اگر شیء درخواست‌شده قبلاً ایجاد نشده باشد، اسپرینگ آن را با استفاده از اطلاعات فایل پیکربندی‌اش ایجاد می‌کند و یک مرجع به آن را به برنامه فراخواننده بازمی‌گرداند. اگر شیء قبلاً ایجاد شده باشد، اسپرینگ به سادگی مرجع شیء موجود را بازمی‌گرداند. این اصل تک‌نسخه‌ای (singleton) است.
  • باید توجه داشت که کلاس تست [Nunit] نام کلاس دسترسی به داده را نمی‌داند. این نام در فایل پیکربندی مشخص شده است. کلاس تست به سادگی یک شیء را درخواست می‌کند که رابط [IArticlesDao] را پیاده‌سازی کرده باشد:
         ' شیء تحت آزمایش
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
...
            articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
        End Sub

این کل هدف Spring است. اگر کلاس پیاده‌سازی را تغییر دهیم، کلاس تست ما نیازی به تغییر نخواهد داشت. ما به سادگی فایل پیکربندی Spring را اصلاح می‌کنیم. خود کلاس تست به جای یک کلاس، صرفاً با یک رابط کار می‌کند.

بیایید این ارائه را با چند نکته عملی به پایان برسانیم.

وقتی می‌نویسیم «اسپرینگ نمونه‌سازی خواهد کرد…»، دقیقاً منظورمان چیست؟ برای پلتفرم .NET، اسپرینگ در سه فایل گنجانده شده است:

Image

برای یک پروژه .NET که با ویژوال استودیو ساخته شده و نیاز به استفاده از Spring دارد، مراحل زیر را دنبال کنید:

  • سه فایل فوق باید در پوشه [bin] پروژه قرار داده شوند
  • [Spring.Core.dll] باید در مراجع پروژه گنجانده شود:

Image

  • کلاس‌هایی که از Spring استفاده می‌کنند، نیاز دارند تا برخی فضاهای نام را وارد کنند، که اغلب شامل موارد زیر است:
Imports Spring.Objects.Factory.Xml

نکته عملی دیگر: فایل پیکربندی Spring باید در کجا قرار داده شود؟ چندین مکان ممکن وجود دارد. یکی از آنها پوشه [bin] در داخل پروژه است. این همان جایی است که فایل [spring-config-plainodbc.xml] از مثال مورد مطالعه در آن قرار داده شده است.