2. ماده ۱ – Spring IoC
اهداف این سند:
- برای بررسی گزینههای پیکربندی و یکپارچهسازی چارچوب Spring (http://www.springframework.org)
- تعریف و استفاده از مفهوم IoC (معکوسسازی کنترل)، که با نام تزریق وابستگی نیز شناخته میشود
2.1. پیادهسازی یک برنامهٔ سهلایه با اسپرینگ
بیایید یک برنامه کلاسیک سهلایه را در نظر بگیریم:
![]() |
ما فرض میکنیم که دسترسی به لایههای کسبوکار و DAO توسط رابطهای جاوا کنترل میشود:
- رابط [IArticlesDao] برای لایه دسترسی به داده
- رابط [IArticlesManager] برای لایه کسبوکار
در لایه دسترسی به داده (یا لایه DAO، شیء دسترسی به داده)، معمول است که با یک SGBD و در نتیجه با یک درایور JDBC کار کرد. بیایید اسکلت یک کلاس را که به یک جدول مادی در یک SGBD دسترسی دارد، در نظر بگیریم:
برای انجام عملیاتی روی SGBD، هر متدی به یک شیء [Connection] نیاز دارد که نمایانگر اتصال به پایگاه داده است و از طریق آن دادهها بین پایگاه داده و کد جاوا مبادله میشوند. برای ساخت این شیء، به چهار مورد اطلاعات نیاز است:
نام کلاس درایور JDBC برای SGBD | |
آدرس URL پایگاه داده مورد استفاده | |
هویتی که اتصال تحت آن برقرار میشود | |
رمز عبور این شناسه |
کلاس قبلی ما [ArticlesDaoPlainJdbc] چگونه میتواند این اطلاعات را بهدست آورد؟ امکانات مختلفی وجود دارد:
راه حل ۱ – اطلاعات بهصورت کد سخت در کلاس درج شده است:
نقطهضعف این راهحل این است که هرگاه این اطلاعات تغییر کند، برای مثال هنگام تغییر رمز عبور، باید کد جاوا اصلاح شود.
راه حل ۲ – اطلاعات هنگام ایجاد شی به آن منتقل میشود:
در اینجا، شیء هنگام ساخت، اطلاعات مورد نیاز برای عملکرد خود را دریافت میکند. مشکل سپس به کدی منتقل میشود که این چهار مورد اطلاعات را به آن ارسال کرده است. این کد چگونه آنها را به دست آورده است؟ کلاس زیر، [ArticlesManagerWithDataBase]، از لایه کسبوکار میتواند یک شیء، [ArticlesDaoPlainJdbc]، از لایه دسترسی به دادهها بسازد:
![]() |
میتوانیم ببینیم که، بار دیگر، اطلاعات مورد نیاز برای ساختن شی [ArticlesDaoPlainJdbc] به سازنده شی [ArticlesManagerWithDataBase] ارائه میشود. میتوانیم تصور کنیم که این اطلاعات توسط یک لایه سطح بالاتر، مانند لایه رابط کاربری، به آن ارسال میشود. به این ترتیب، گام به گام به بالاترین لایهٔ برنامه میرسیم. این لایه به دلیل موقعیتی که دارد، توسط هیچ لایهای که بتواند اطلاعات پیکربندی مورد نیازش را منتقل کند، فراخوانی نمیشود. بنابراین باید جایگزینی برای پیکربندی از طریق سازنده پیدا کنیم. راهحل معمول برای پیکربندی یک برنامه در لایه سطح بالای آن، استفاده از فایلی است که حاوی تمام اطلاعاتی است که احتمالاً در طول زمان تغییر میکنند. ممکن است چندین فایل از این دست وجود داشته باشد. هنگامی که برنامه شروع میشود، یک لایه راهاندازی سپس همه یا برخی از اشیاء مورد نیاز لایههای مختلف برنامه را ایجاد خواهد کرد.
انواع مختلفی از فایلهای پیکربندی وجود دارد. روند کنونی استفاده از فایلهای XML است. این رویکردی است که Spring اتخاذ کرده است. فایلی که یک شیء [ArticlesDaoPlainJdbc] را پیکربندی میکند ممکن است به این شکل باشد:
یک برنامه مجموعهای از اشیاء است که اسپرینگ آنها را «بیان» (beans) مینامد، زیرا آنها از استاندارد JavaBean برای نامگذاری گترها و سترهای فیلدهای خصوصی یک شیء پیروی میکنند. اشیاء درون یک برنامه که نقششان ارائه یک سرویس است، اغلب به صورت یک نمونه واحد ایجاد میشوند. اینها تکنسخه (singletons) نامیده میشوند. بنابراین، در مثال برنامه چندلایهای که در اینجا بررسی میشود، دسترسی به پایگاه داده محصولات توسط یک نمونه واحد از کلاس [ArticlesDaoPlainJdbc] مدیریت خواهد شد. در یک برنامه وب، این اشیاء سرویس همزمان به چندین مشتری خدمترسانی میکنند. برای هر مشتری یک شیء سرویس ایجاد نمیشود.
فایل پیکربندی Spring فوق اجازه میدهد تا یک شیء سرویس از نوع [ArticlesDaoPlainJdbc] در بستهای به نام [istia.st.articles.dao] ایجاد شود. چهار مورد اطلاعات مورد نیاز برای سازنده این شیء در داخل یک تگ <bean>...</bean>** تعریف شدهاند. به ازای هر تعداد singleton که باید ایجاد شوند، به همان تعداد تگ <bean>** وجود خواهد داشت.
اشیاء تعریفشده در فایل Spring چه زمانی ساخته میشوند؟ инициализация یک برنامه میتواند به متد اصلی (main) آن برنامه، در صورت وجود، واگذار شود. برای یک برنامه وب، این ممکن است متد [init] از servlet اصلی باشد. در هر برنامهای، متدی وجود دارد که تضمین میشود اولین متدی باشد که اجرا میشود. معمولاً در همین متد است که اشیاء تکنسخهای ایجاد میشوند.
یک مثال بزنیم. فرض کنید میخواهیم کلاس قبلی [ArticlesDaoPlainJdbc] را با استفاده از یک تست JUnit تست کنیم. یک کلاس تست به نام JUnit دارای متدی به نام [setUp] است که قبل از هر متد دیگری اجرا میشود. اینجاست که سینگلتون [ArticlesDaoPlainJdbc] ایجاد خواهد شد.
اگر رویکرد ارسال اطلاعات پیکربندی از طریق سازنده را دنبال کنیم، کلاس تست زیر را خواهیم داشت:
کلاس فراخواننده [TestArticlesPlainJdbc] باید چهار اطلاعات مورد نیاز برای راهاندازی نمونهٔ واحد [ArticlesDaoPlainJdbc] را که قرار است ساخته شود، بداند.
اگر رویکرد ارسال اطلاعات پیکربندی از طریق فایل پیکربندی را دنبال کنیم، میتوانیم کلاس تست زیر را با استفاده از فایل Spring که در بالا توضیح داده شد، داشته باشیم.
در اینجا، کلاس فراخواننده [TestSpringArticlesPlainJdbc] نیازی ندارد که اطلاعات مورد نیاز برای راهاندازی singleton را بداند. این کلاس صرفاً باید بداند:
- [springArticlesPlainJdbc.xml]: نام فایل پیکربندی Spring که در بالا توضیح داده شد
- [articlesDao]: نام singleton که باید ایجاد شود
هرگونه تغییر در فایل پیکربندی، به جز این دو عنصر، تأثیری بر کد جاوا ندارد. این روش پیکربندی اشیاء یک برنامه بسیار انعطافپذیر است. برای پیکربندی خود، برنامه تنها به دو چیز نیاز دارد:
- نام فایل Spring که حاوی تعاریف singletonهای قابل ایجاد است
- نام این سنگلتونها، که کد جاوا برای بهدستآوردن مرجع اشیایی که از طریق فایل پیکربندی با آنها مرتبط شدهاند، از آن استفاده میکند
2.2. تزریق وابستگی و معکوسسازی کنترل
اکنون بیایید مفهوم تزریق وابستگی (Dependency Injection) را که توسط Spring برای پیکربندی برنامهها استفاده میشود، معرفی کنیم. از اصطلاح معکوسسازی کنترل (Inversion of Control) نیز استفاده میشود. ایجاد singleton [ArticlesManagerWithDataBase] در لایه کسبوکار برنامه ما را در نظر بگیرید:
![]() |
برای دسترسی به دادهها در SGBD، لایه کسبوکار باید از خدمات ابجکتی که رابط [IArticlesDao] را پیادهسازی میکند، استفاده کند؛ برای مثال، ابجکتی از نوع [ArticlesDaoPlainJdbc]. کد کلاس [ArticlesManagerWithDataBase] ممکن است چیزی شبیه به این باشد:
public class ArticlesManagerWithDataBase implements IArticlesManager {
//یک نمونه دسترسی به داده
private IArticlesDao articlesDao;
....
public ArticlesManagerWithDataBase (String driverClassName, String url, String user, String pwd, ...) {
...
// ایجاد سرویس دسترسی به دادهها
articlesDao =(IArticlesDao)new ArticlesDaoPlainJdbc(driverClassName,url,user,pwd);
...
}
public ... doSomething(...){
...
}
}
فرض میشود کلاس [ArticlesDaoPlainJdbc] در اینجا رابط [IArticlesDao] را پیادهسازی میکند:
برای ایجاد نمونهٔ واحد (singleton) از نوع [IArticlesDao] که برای عملکرد کلاس مورد نیاز است، سازندهٔ آن صراحتاً از نام کلاسی که رابط [IArticlesDao] را پیادهسازی میکند، استفاده میکند:
بنابراین وابستگی سختکدشدهای به نام کلاس در کد وجود دارد. اگر کلاسی که رابط [IArticlesDao] را پیادهسازی میکند تغییر کند، کد سازنده فوق باید اصلاح شود. روابط زیر بین اشیاء وجود دارد:
![]() |
کلاس [ArticlesManagerWithDataBase] خود ابتکار عمل را برای ایجاد شیء [ArticlesDaoPlainJdbc] که به آن نیاز دارد، به دست میگیرد. با بازگشت به اصطلاح «معکوسسازی کنترل»، میتوان گفت که این کلاس است که «کنترل» ایجاد شیء مورد نیازش را در اختیار دارد.
اگر بخواهیم برای کلاس [ArticlesManagerWithDataBase] یک کلاس تست JUnit بنویسیم، ممکن است چیزی شبیه به این باشد:
کلاس تست یک نمونه از کلاس کسبوکار [ArticlesManagerWithDataBase] ایجاد میکند که به نوبه خود، در داخل سازندهاش، یک نمونه از کلاس دسترسی به دادهها [ArticlesDaoPlainJdbc] ایجاد میکند.
راه حل اسپرینگ نیاز کلاس کسبوکار [ArticlesManagerWithDataBase] به دانستن نام کلاس دسترسی به داده [ArticlesDaoPlainJdbc] را که به آن نیاز دارد، از بین میبرد. این امر اجازه میدهد تا نام بدون تغییر دادن کد جاوا کلاس کسبوکار تغییر کند. اسپرینگ امکان ایجاد همزمان هر دو نوع singleton – یکی برای لایه دسترسی به داده و دیگری برای لایه کسبوکار – را فراهم میکند. فایل پیکربندی اسپرینگ یک bean جدید را تعریف خواهد کرد:
ویژگی جدید در تعریف یک بیون سینگلتون برای کلاس کسبوکار است که باید ایجاد شود:
<bean id="articlesManager" class="istia.st.articles.domain.ArticlesManagerWithDataBase">
<property name="articlesDao">
<ref bean="articlesDao"/>
</property>
</bean>
- کلاسی که دانه [articlesManager] را پیادهسازی میکند تعریف شده است: [ArticlesManagerWithDataBase]
- میدان [articlesDao] از بیون از طریق تگ <property name="articlesDao"> مقداردهی میشود. این میدانی است که در کلاس [ArticlesManagerWithDataBase] تعریف شده است:
برای اینکه فیلد [articlesDao] توسط Spring و تگ <property> آن مقداردهی اولیه شود، این فیلد باید مطابق با استاندارد JavaBean باشد و باید یک متد [setArticlesDao] برای مقداردهی اولیه فیلد [articlesDao] وجود داشته باشد. توجه داشته باشید که نام متد به طور بسیار دقیقی از نام فیلد استخراج میشود. به همین ترتیب، اغلب یک متد [get...] برای بازیابی مقدار فیلد وجود دارد. در اینجا، این متد [getArticlesDao] است. در این نسخه جدید، کلاس [ArticlesManagerWithDataBase] دیگر کانتراکتور ندارد. دیگر به آن نیازی نیست.
- مقداری که Spring به فیلد [articlesDao] اختصاص میدهد، همان مقدار bean [articlesDao] است که در فایل پیکربندی آن تعریف شده است:
<bean id="articlesManager" class="istia.st.articles.domain.ArticlesManagerWithDataBase">
<property name="articlesDao">
<ref bean="articlesDao"/>
</property>
</bean>
<bean id="articlesDao" class="istia.st.articles.dao.ArticlesDaoPlainJdbc">
<constructor-arg index="0">
.............
</bean>
- هنگامی که اسپرینگ نمونهٔ واحد [ArticlesManagerWithDataBase] را ایجاد میکند، نمونهٔ واحد [ArticlesDaoPlainJdbc] را نیز ایجاد خواهد کرد:
- اسپرینگ یک گراف وابستگی از بینها ایجاد میکند و میبیند که بین [articlesManager] به بین [articlesDao] وابسته است
- این بیون [articlesDao] را میسازد، یعنی یک شیء از نوع [ArticlesDaoPlainJdbc]
- سپس بیون [articlesManager] از نوع [ArticlesManagerWithDataBase] را ایجاد خواهد کرد
حال بیایید یک تست JUnit برای کلاس [ArticlesManagerWithDataBase] را تصور کنیم. ممکن است چیزی شبیه به این باشد:
بیایید فرایند ایجاد دو تکنسخهای را که در فایل Spring با نام [springArticlesManagerWithDataBase.xml] تعریف شدهاند، دنبال کنیم.
- متد [setUp] در بالا یک مرجع به بین با نام [articlesManager] را درخواست میکند
- اسپرینگ به فایل پیکربندی خود مراجعه میکند و دانه (bean) با نام [articlesManager] را پیدا میکند. اگر این دانه قبلاً ایجاد شده باشد، به سادگی مرجعی به آن شیء (singleton) بازمیگرداند؛ در غیر این صورت، آن را ایجاد میکند.
- اسپرینگ تشخیص میدهد که بین [articlesManager] به بین [articlesDao] وابسته است. بنابراین، اگر این اشیاء قبلاً ایجاد نشده باشند، اشیاء تکنسخه (singleton) [articlesDao] از نوع [ArticlesDaoPlainJdbc] ایجاد میشوند.
- این مکانیزم میتواند به صورت زیر خلاصه شود:
این مکانیزم را میتوان به صورت زیر نمودار کرد:
![]() |
بیایید اسکلت کلاس [ArticlesManagerWithDataBase] را به یاد بیاوریم:
هنگامی که اسپرینگ ساخت اشیاء تکنسخه (singletons) را به پایان رساند، ما یک شیء از نوع [ArticlesManagerWithDataBase] داریم که فیلد [articlesDao] آن بدون اطلاع خود او مقداردهی اولیه شده است. ما میگوییم که یک وابستگی به شیء [ArticlesManagerWithDataBase] تزریق شده است. همچنین گفته میشود که کنترل معکوس شده است: دیگر شیء [ArticlesManagerWithDataBase] نیست که ابتکار عمل را برای ایجاد به تنهایی شیء پیادهساز رابط [IArticlesDao] که به آن نیاز دارد، به دست میگیرد؛ بلکه خودِ برنامهٔ سطح بالا (هنگام راهاندازی) است که مسئولیت ایجاد تمام اشیاء مورد نیاز و مدیریت وابستگیهای بین آنها را بر عهده میگیرد.
مزیت اصلی پیکربندی تکنسخهای [ArticlesManagerWithDataBase] از طریق یک فایل Spring این است که اکنون میتوانیم کلاس پیادهسازی متناظر با فیلد [articlesDao] از کلاس [ArticlesManagerWithDataBase] را بدون تغییر کد خودِ کلاس، تغییر دهیم. کافی است نام کلاس را در تعریف bean [articlesDao] در فایل Spring تغییر دهید:
به عنوان مثال، به این صورت درخواهد آمد:
بیان [ArticlesManagerWithDataBase] بدون اینکه حتی متوجه شود، با این کلاس جدید دسترسی به دادهها کار خواهد کرد.
2.3. Spring IoC در عمل
2.3.1. مثال ۱
کلاس زیر را در نظر بگیرید:
کلاس دارای:
- دو فیلد خصوصی، 'name' و 'age'
- متدهای get و set برای این دو فیلد
- یک متد `toString` برای بازیابی مقدار شی `[Personne]` به صورت یک رشته
- یک متد `init` که هنگام ایجاد شی توسط Spring فراخوانی میشود، و یک متد `close` که هنگام تخریب شی فراخوانی میشود
برای ایجاد اشیاء از نوع [Personne]، از فایل Spring زیر استفاده خواهیم کرد:
این فایل با نام config.xml فراخوانی خواهد شد.
- این دو بیون را با کلیدهای «person1» و «person2» از نوع [Personne] تعریف میکند.
- این فایل فیلدهای [nom, age] را برای هر شخص مقداردهی اولیه میکند
- این کلاس متدهایی را تعریف میکند که در هنگام ساخت اولیهٔ شیء [init-method] و در هنگام تخریب شیء [destroy-method] فراخوانی میشوند
برای آزمایشهایمان از یک کلاس آزمایشی واحد به نام JUnit استفاده خواهیم کرد که به آن به ترتیب متدهایی اضافه خواهیم کرد. نسخهٔ اول این کلاس به شرح زیر خواهد بود:
توضیحات:
- برای بازیابی بینهای تعریفشده در فایل [config.xml]، از یک شیء از نوع [ListableBeanFactory] استفاده میکنیم. انواع شیء دیگری نیز وجود دارند که امکان دسترسی به بینها را فراهم میکنند. شیء [ListableBeanFactory] در متد [setUp] از کلاس تست به دست آمده و در یک متغیر خصوصی ذخیره میشود. بنابراین، این شیء برای تمام متدهای تست در دسترس خواهد بود.
- فایل [config.xml] در [ClassPath] و c.a.d برنامه، در یکی از دایرکتوریهایی که ماشین مجازی جاوا هنگام جستجوی کلاسی که برنامه به آن ارجاع داده است، جستجو میکند، قرار داده خواهد شد. ابژه [ClassPathResource] برای جستجو در میان منابع داخل [ClassPath] یک برنامه، در این مورد فایل [config.xml]، استفاده میشود.
- اسپرینگ میتواند از فایلهای پیکربندی در فرمتهای مختلف استفاده کند. شیء [XmlBeanFactory] برای تجزیه یک فایل پیکربندی در فرمت XML استفاده میشود.
- پارس کردن یک فایل Spring یک شی از نوع [ListableBeanFactory] را بازمیگرداند، که در این مورد شی bf است. با استفاده از این شی، یک بن (bean) با کلید C را میتوان از طریق bf.getBean(C) بازیابی کرد.
- متد [test1] مقادیر بیینهایی با کلیدهای «person1» و «person2» را بازیابی و نمایش میدهد.
ساختار پروژه اکلیپس برنامه ما به شرح زیر است:

توضیحات:
- پوشه [src] حاوی کد منبع است. کد کامپایلشده در پوشهای به نام [bin] قرار خواهد گرفت که در اینجا نشان داده نشده است.
- فایل [config.xml] در ریشه پوشه [src] قرار دارد. هنگامی که پروژه ساخته میشود، به طور خودکار به پوشه [bin] کپی میشود که بخشی از پوشه [ClassPath] برنامه را تشکیل میدهد. این همان جایی است که توسط شیء [ClassPathResource] قرار دارد.
- پوشه [lib] شامل سه کتابخانه جاوا مورد نیاز برنامه است:
- commons-logging.jar و spring-core.jar برای کلاسهای Spring
- junit.jar برای کلاسهای JUnit
- پوشه [lib] نیز بخشی از [ClassPath] برنامه است
اجرای متد [test1] از تست JUnit نتایج زیر را به دست میدهد:
توضیحات:
- Spring با استفاده از کتابخانه [commons-logging.jar] تعدادی رویداد را ثبت میکند. این لاگها به ما امکان میدهند تا بهتر درک کنیم که Spring چگونه کار میکند.
- فایل [config.xml] بارگذاری و سپس پردازش شد
- عملیات*
ایجاد دانه [personne1] را تحریک کرد. ما میتوانیم ورودی لاگ Spring مربوط به این موضوع را ببینیم. از آنجا که تعریف بیون [personne1] حاوی متن '[init-method="init"]' بود، متد [init] از شیء ایجاد شده [Personne] اجرا شد. پیام مربوطه نمایش داده میشود.
- عملیات
موجب نمایش مقدار شیء ایجاد شده [Personne] شد.
- همان پدیده برای دانه کلیدی [personne2] رخ میدهد.
- عملیات آخر
personne2 = (Personne) bf.getBean("personne2");
System.out.println("personne2=" + personne2.toString());
منجر به ایجاد یک شیء جدید از نوع [Personne] نشد. اگر چنین بود، متد [init] نمایش داده میشد، که در اینجا اینطور نیست. این اصل سینگلتون است. به طور پیشفرض، اسپرینگ تنها یک نمونه از بی́nها را در فایل پیکربندی خود ایجاد میکند. این یک سرویس ارجاع شیء است. اگر برای یک شیء که هنوز ایجاد نشده است درخواست ارجاع داده شود، آن را ایجاد کرده و ارجاع آن را برمیگرداند. اگر شیء قبلاً ایجاد شده باشد، اسپرینگ به سادگی ارجاع آن را برمیگرداند.
- شایان ذکر است که هیچ اثری از متد [close] از شی [Personne] وجود ندارد، هرچند ما [destroy-method=close] را در تعریف بین (bean) نوشته بودیم. ممکن است این متد تنها زمانی اجرا شود که حافظه اشغالشده توسط شی توسط جمعکننده زباله (garbage collector) آزاد گردد. تا زمانی که این اتفاق میافتد، برنامه از قبل پایان یافته و نوشتن روی صفحه هیچ تأثیری ندارد. باید بررسی شود.
اکنون که مبانی پیکربندی Spring را پوشش دادهایم، میتوانیم توضیحات را کمی سریعتر پیش ببریم.
2.3.2. مثال ۲
بیایید کلاس جدید زیر، [Voiture] را در نظر بگیریم:
کلاس شامل:
- سه فیلد خصوصی: type، make و owner. این فیلدها را میتوان با استفاده از متدهای عمومی get و set بیون اولیه کرد و خواند. همچنین میتوان آنها را با استفاده از سازنده Car(String, String, Person) اولیه کرد. این کلاس همچنین دارای یک سازنده بدون آرگومان برای انطباق با استاندارد JavaBean است.
- یک متد toString برای بازیابی مقدار شی [Voiture] به صورت یک رشته
- یک متد `init` که بلافاصله پس از ایجاد شی توسط Spring فراخوانی میشود، و یک متد `close` که هنگام تخریب شی فراخوانی خواهد شد
برای ایجاد اشیاء از نوع [Voiture]، از فایل Spring زیر [config.xml] استفاده خواهیم کرد:
این فایل یک بیون با کلید "car1" و نوع [Voiture] را به تعاریف قبلی اضافه میکند. برای مقداردهی اولیه این بیون میتوانست نوشت:
به جای انتخاب این متد که پیشتر معرفی شده است، در اینجا تصمیم گرفتهایم از سازنده کلاس Voiture(String, String, Personne) استفاده کنیم. علاوه بر این، بین [voiture1] متدی را که باید در حین ساخت اولیه شیء [init-method] فراخوانی شود و متدی را که باید در حین تخریب شیء [destroy-method] فراخوانی شود، تعریف میکند.
برای آزمایشهایمان، از کلاس تست JUnit که قبلاً معرفی شده است، استفاده خواهیم کرد و متد زیر [test2] را به آن اضافه میکنیم:
متد [test2]، بیون [voiture1] را بازیابی کرده و آن را نمایش میدهد.
ساختار پروژهٔ Eclipse همانند تست قبلی باقی میماند. اجرای متد [test2] از تست JUnit نتایج زیر را تولید میکند:
توضیحات:
- متد [test2] یک مرجع به بین [voiture1] را درخواست میکند
- خط ۴: اسپرینگ شروع به ایجاد بیون [voiture1] میکند زیرا این بیون هنوز ایجاد نشده است (singleton)
- خط ۶: از آنجا که بین [voiture1] به بین [personne2] ارجاع میدهد، بین دوم نیز به نوبه خود ایجاد میشود
- خط ۷: دانه [personne2] ایجاد شده است. سپس متد [init] آن اجرا میشود.
- خط ۹: Spring نشان میدهد که از یک سازنده برای ایجاد bean [voiture1] استفاده خواهد کرد
- خط ۱۰: دانه [voiture1] ایجاد شده است. متد آن [init] سپس اجرا میشود.
- خط ۱۱: متد [test2] مقدار بن [voiture1] را نمایش میدهد
2.3.3. مثال ۳
ما کلاس جدید زیر را معرفی میکنیم، [GroupePersonnes]:
دو عضو خصوصی آن عبارتند از:
اعضا: آرایهای از افرادی که عضو گروه هستند
groupesDeTravail: یک دیکشنری که یک شخص را به یک گروه کاری نگاشت میکند
در اینجا باید توجه داشت که کلاس [GroupePersonnes] مطابق با استاندارد JavaBean، یک سازنده بدون آرگومان تعریف نمیکند. باید به خاطر داشت که در صورت عدم وجود هرگونه سازنده، یک سازنده «پیشفرض» وجود دارد که همان سازنده بدون آرگومان است و هیچ کاری انجام نمیدهد.
هدف در اینجا نشان دادن این است که چگونه Spring امکان инициалиزهسازی اشیاء پیچیده، مانند اشیائی که دارای فیلدهای از نوع آرایه یا دیکشنری هستند، را فراهم میکند. ما یک bean جدید به فایل قبلی Spring با شناسه [config.xml] اضافه میکنیم:
- تگ <list> به شما امکان میدهد یک فیلد از نوع آرایه یا فیلدی که رابط List را پیادهسازی میکند، با مقادیر مختلف مقداردهی اولیه کنید.
- تگ <map> به شما امکان میدهد همین کار را با فیلدی که رابط Map را پیادهسازی میکند، انجام دهید
برای تستهایمان، از کلاس تست JUnit که قبلاً معرفی شده است استفاده خواهیم کرد و متد زیر [test3] را به آن اضافه میکنیم:
متد [test3] بیان [groupe1] را بازیابی کرده و نمایش میدهد.
ساختار پروژهٔ Eclipse همانند تست قبلی باقی میماند. اجرای متد [test3] از تست JUnit نتایج زیر را تولید میکند:
توضیحات:
- متد [test3] یک مرجع به بین [groupe1] را درخواست میکند
- خط ۴: اسپرینگ شروع به ایجاد این بین میکند
- زیرا بین [groupe1] به بینهای [personne1] و [personne2] ارجاع میدهد؛ این دو بین ایجاد میشوند (خطوط ۶ و ۹) و متدهای init آنها اجرا میشوند (خطوط ۷ و ۱۰)
- خط ۱۱: بیون [groupe1] ایجاد شده است. متد آن [init] اکنون اجرا میشود.
- خط ۱۲: نمایش درخواستی توسط متد [test3].
2.4. اسپرینگ برای پیکربندی برنامههای وب سهلایه
2.4.1. معماری کلی برنامه
ما میخواهیم یک برنامه سهلایه با ساختار زیر بسازیم:
![]() |
- سه لایه از طریق استفاده از رابطهای جاوا مستقل خواهند شد
- ادغام این سه لایه توسط Spring انجام خواهد شد
- ما برای هر یک از سه لایه، بستههای جداگانهای ایجاد خواهیم کرد که آنها را Control، Domain و Dao مینامیم. یک بستهٔ اضافی حاوی برنامههای تست خواهد بود.
ساختار برنامه در اکلیپس میتواند به صورت زیر باشد:

2.4.2. لایه دسترسی به داده DAO
لایه DAO رابط زیر را پیادهسازی خواهد کرد:
package istia.st.demo.dao;
public interface IDao1 {
public int doSometingInDaoLayer(int a, int b);
}
- دو کلاس Dao1Impl1 و Dao1Impl2 را بنویسید که رابط IDao1 را پیادهسازی میکنند. متد Dao1Impl1.doSomethingInDaoLayer مقدار a+b را برمیگرداند و متد Dao1Impl2.doSomethingInDaoLayer مقدار a-b را برمیگرداند.
- یک کلاس تست JUnit بنویسید تا دو کلاس قبلی را تست کند
2.4.3. لایه کسبوکار
لایه کسبوکار رابط زیر را پیادهسازی خواهد کرد:
package istia.st.demo.domain;
public interface IDomain1 {
public int doSomethingInDomainLayer(int a, int b);
}
- دو کلاس Domain1Impl1 و Domain1Impl2 را بنویسید که رابط IDomain1 را پیادهسازی میکنند. این کلاسها دارای یک سازنده خواهند بود که پارامتر از نوع IDao1 را میپذیرد. متد Domain1Impl1.doSomethingInDomainLayer مقدار a و b را یک واحد افزایش میدهد، سپس این دو پارامتر را به متد doSomethingInDaoLayer شیء از نوع IDao1 ارسال میکند. از سوی دیگر، متد Domain1Impl2.doSomethingInDomainLayer قبل از انجام همان کار، مقادیر a و b را یک واحد کاهش میدهد.
- یک کلاس تست JUnit برای تست دو کلاس قبلی بنویسید
2.4.4. لایه رابط کاربری
لایه رابط کاربری رابط زیر را پیادهسازی خواهد کرد:
package istia.st.demo.control;
public interface IControl1 {
public int doSometingInControlLayer(int a, int b);
}
- دو کلاس Control1Impl1 و Control1Impl2 را بنویسید که رابط IControl1 را پیادهسازی میکنند. این کلاسها دارای یک سازنده خواهند بود که پارامتر از نوع IDomain1 را میپذیرد. متد Control1Impl1.doSomethingInControlLayer مقدار a و b را یک واحد افزایش میدهد، سپس این دو پارامتر را به متد doSomethingInDomainLayer شیء از نوع IDomain1 ارسال میکند. از سوی دیگر، متد Control11Impl2.doSomethingInControlLayer قبل از انجام همان کار، مقادیر a و b را یک واحد کاهش میدهد.
- یک کلاس تست JUnit برای تست دو کلاس قبلی بنویسید
2.4.5. یکپارچهسازی با Spring
- یک فایل پیکربندی Spring بنویسید که مشخص کند هر یک از سه لایه قبلی باید از کدام کلاسها استفاده کنند
- یک کلاس تست JUnit را با استفاده از پیکربندیهای مختلف Spring بنویسید تا انعطافپذیری برنامه را برجسته کنید
- یک برنامه مستقل (با متد main) بنویسید که دو پارامتر را به رابط IControl1 ارسال کرده و نتیجه بازگردانده شده توسط این رابط را نمایش دهد.
2.4.6. یک راهحل
2.4.6.1. پروژه اکلیپس

آرشیوهای پوشه [lib] به پروژه [ClassPath] اضافه شدهاند.
2.4.6.2. پکیج [istia.st.demo.dao]
رابط:
یک کلاس پیادهسازی اولیه:
یک کلاس پیادهسازی دوم:
2.4.6.3. پکیج [istia.st.demo.domain]
رابط:
یک کلاس پیادهسازی اول:
یک کلاس پیادهسازی دوم:
2.4.6.4. پکیج [istia.st.demo.control]
رابط
یک کلاس پیادهسازی اول:
یک کلاس پیادهسازی دوم:
2.4.6.5. فایلهای پیکربندی [Spring]
یک [springMainTest1.xml] اول:
یک [springMainTest2.xml] دوم:
2.4.6.6. بسته تست [istia.st.demo.tests]
آزمایشی از نوع [main]:
نتایج نمایشدادهشده در کنسول اکلیپس:
آزمون دیگری با استفاده از فایل پیکربندی دوم [Spring]:
نتایج نمایشدادهشده در کنسول اکلیپس:
در نهایت، یک تست JUnit:
2.5. 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 انجام میدهد.
![]() |
این رویکرد مزایای متعددی دارد:
- پس از تعریف فیلتر، میتوان آن را به چندین متد اعمال کرد، برای مثال به همه متدهایی که به یک تراکنش نیاز دارند.
- روشهایی که به این صورت فیلتر میشوند نیازی به بازنویسی ندارند
- از آنجا که فیلترهای مورد استفاده از طریق پیکربندی تعریف میشوند، میتوان آنها را تغییر داد
علاوه بر مفاهیم IoC و AOP، اسپرینگ کلاسهای پشتیبانی متعددی را برای برنامههای کاربردی سهلایه فراهم میکند:
- برای JDBC، SqlMap (iBATIS)، و برای Hibernate، JDO (Java Data Object) در لایه DAO
- برای مدل MVC در لایه رابط کاربری
برای اطلاعات بیشتر: http://www.springframework.org.









