ظرف IoC – قسمت 2 از 3 – Spring MVC

تعریف ابتدایی bean

تعریف یک bean شامل اطلاعاتی است که باید در اختیار ظرف قرار گرفته تا بداند: ۱- چگونه باید یک نمونه bean از آن ایجاد کند، ۲- از جزئیات چرخه حیات آن اطلاع پیدا کند و ۳- در مورد وابستگی های bean اطلاع حاصل کند. این قسمت در مورد دو جنبه اول مطالبی ارائه شده است.

شناسه

در راستای ایجاد یک تعریف سطح بالای bean، اکثر اوقات نیاز است یک یا چند شناسه و یا نام برای bean در نظر گرفته شود، به طوری که سایر اشیاء bean بتوانند از این طریق به آن مراجعه کرده و یا بتوان در هنگام استفاده از ظرف به صورت برنامه نویسی به آن مراجعه کرد. برای تعریف یک شناسه از خصیصه id استفاده کنید (و اگر چند شناسه وجود داشته باشد، شناسه اصلی را به کار بگیرید). از آن جا که این خصیصه دارای نوع XML IDREF می باشد، هنگامی که اشیاء bean دیگر بخواهند به این bean اشاره کنند، پارسر XML خواهد توانست معتبر بودن این ارجاع را تشخیص دهد (برای معتبر بودن ارجاع، باید bean دوم نیز در همان فایل وجود داشته باشد)، که در نتیجه به شما اجازه اعتبارسنجی قبل از پیکربندی کارخانه را می دهد. شناسه های XML IDREF در مورد کاراکترهای مجاز مقداری محدودیت دارند، به این صورت که باید با یک حرف شروع شده و در ادامه تنها از کاراکترهای الفبایی، عددی و یا زیرخط[۱]استفاده کنند، و هیچ فضای خالی در میان آن ها نباشد. هرچند این محدودیت معمولا مشکلی ایجاد نمی کند، اما برای دور زدن این محدودیت، می توان از صفت name برای تهیه شناسه استفاده کرد. این امکان، برای مثال در زمانی مفید است که شناسه bean، به طور کامل تحت کنترل کاربر نبوده و یک مسیر URL را نشان دهد. علاوه بر این، خصیصه name اجازه می دهد لیستی از شناسه ها که با کاما از هم جدا شده اند تهیه شود. هنگامی که از طریق ترکیب خصیصه id و/یا صفت name بیش از یک شناسه در یک تعریف bean مشخص شده باشد، شناسه های بعد از شناسه اول را می توان به عنوان نام مستعار در نظر گرفت. اعتبار تمام شناسه ها در هنگام اشاره به bean، یکسان است. به مثال زیر توجه کنید:

 

 

شی bean سوم به شناسه ای نیاز دارد که با / شروع می شود، بنابراین نمی تواند از خصیصه id استفاده کرده و به جای آن باید از name استفاده کند. توجه کنید که bean چهارم سه شناسه داشته که همه دارای اعتبار یکسان هستند. ممکن است شما از خود بپرسید که چه نیازی به تهیه بیش از یک شناسه برای یک bean وجود دارد. برای مثال از چند شناسه زمانی استفاده می کنید که بخواهید پیکربندی خود را بر اساس مولفه ها و یا ماژول ها تقسیم بندی کنید، به طوری که هر یک از مؤلفه ها فایل XML مخصوص خود را داشته باشد و اشیاء bean و وابستگی های مربوط به آن مولفه در این فایل قرار بگیرد. نام این وابستگی ها می تواند (به عنوان مثال) با پیشوند خاص آن مولفه شروع شود، همانند کاری که برای DataSource فرضی در نمونه کد قبلی انجام شد. هنگامی که کارخانه bean نهایی یا بستر برنامه از چند فایل مجزا ساخته شود (یا همانطور که در ادامه توضیح داده می شود، یک سلسله مراتب بستر برنامه ایجاد شود)، هر مولفه به bean واقعی که نیاز دارد اشاره خواهد کرد. این، یکی از راه های جدا سازی بین مولفه ها است.

مکانیسم ایجاد bean

هنگامی که به یک نمونه bean نیاز باشد، باید نحوه ایجاد و یا دست یابی به آن برای ظرف مشخص شود. ایجاد bean از طریق سازنده آن، بیشترین کاربرد را دارد. خصیصه class برای تعیین نام کلاس bean استفاده می شود. زمانی که ظرف به یک نمونه جدید از bean نیاز داشته باشد، درون خود عملکردی معادل new در زبان جاوا را انجام می دهد. تمام نمونه هایی که ما تاکنون دیده ایم از این مکانیزم استفاده می کنند.

در روش دیگر به ظرف گفته می شود از یکی از متدهای ایستای[۲] کارخانه برای تهیه یک نمونه جدید bean استفاده کند. در صورتی که از کدهای قدیمی تر استفاده می کنید، هیچ کنترلی بر روی آن ها وجود ندارد و گاهی اوقات شما را مجبور به استفاده از چنین متدهایی می کند. از خصیصه class برای مشخص کردن نام کلاسی که متد ایستای کارخانه را دربرمی گیرد استفاده کرده و نام خود متد را از طریق خصیصه factory-method تعیین کنید:

 

 

متد ایستای کارخانه یک نمونه از نوع مورد نظر را برمی گرداند. باید دقت داشت کلاسی که این نمونه آن روی آن ایجاد می شود، نباید همان کلاسی باشد که شامل متد کارخانه است.

مکانیسم سوم ایجاد یک نمونه bean توسط ظرف این است که، ظرف از یک متد غیرایستای کارخانه استفاده کند:

 

 

 

هرگاه نیاز به ایجاد یک نمونه جدید از testBeanObtainedViaNonStaticFactory باشد، ابتدا یک نمونه از nonStaticFactory ایجاد شده و سپس متد getTestBeanInstance از این نمونه فراخوانی می شود. توجه داشته باشید که در این روش، برای خصیصه class مقداری مشخص نشده است.

هنگامی که یک شی جدید تهیه می شود، ظرف بدون توجه به این که آن شی از طریق یک سازنده، متد ایستای کارخانه و یا متد غیرایستای کارخانه بدست آمده باشد، با آن رفتار یکسانی خواهد داشت. بنابراین از تزریق قراردهنده استفاده شده و متدهای callback مورد نیاز در چرخه حیات عادی بر روی آن اعمال می شوند.

اشیاء bean یگانه و غیریگانه

یکی از ویژگی های مهم در چرخه حیات یک bean، که باید توسط ظرف دیده شود، یگانگی آن است. اشیا bean یگانه تنها یک بار توسط ظرف ایجاد می شوند. سپس ظرف آن شی را نگه داری کرده و زمانی که مجددا به آن اشاره شود، همان نمونه را برمی گرداند. این کار می تواند از نظر استفاده از منابع (حافظه یا حتی CPU) نسبت به زمانی که برای هر درخواست یک نمونه جدید bean ایجاد می شود، به طور قابل توجهی ارزان تر تمام شود. به این ترتیب، زمانی که در پیاده سازی واقعی کلاس ها این امکان را وجود داشته باشد، یعنی شی ایجاد شده از این کلاس ها stateless بوده و یا حالت آن تنها یک بار و در زمان مقداردهی اولیه تنظیم شود، این گزینه انتخاب بهتری است. در این حالت، شی threadsafe بوده و می تواند در یک زمان توسط چند نخ استفاده شود. اشیاء bean به طور پیش فرض یگانه هستند، چرا که در عمل بسیاری از سرویس ها، کنترلرها، و منابعی که درون ظرف پیکره بندی می شوند به صورت کلاس های threadsafe پیاده سازی شده و پس از زمان مقداردهی اولیه هیچ حالتی را درون خود نگه نمی دارند.

همان طور که از اسم اشیاء bean غیریگانه برمی آید، در این اشیا خصیصه singleton برابر مقدار false تنظیم شده است. باید توجه کرد که چرخه حیات یک bean غیریگانه بسیار متفاوت از یک bean یگانه است. هنگامی که از یک ظرف خواسته می شود که یک bean غیریگانه تهیه کند، آن شی ایجاد و استفاده می شود، اما ظرف آن را نگه نخواهد داشت. بنابراین در حالی که این امکان وجود دارد که اسپرینگ برخی عملیات پایان چرخه حیات را بر روی یک bean یگانه انجام دهد، اما برای اشیاء bean غیریگانه این نوع عملیات باید در کد کاربر انجام شوند، زیرا از لحظه ساخت این اشیا به بعد، دیگر ظرف چیزی در مورد آن ها نگه نخواهد داشت:

 

 

تعیین وابستگی های bean

تهیه وابستگی های یک شی bean، که شامل اشیاء bean دیگر و یا مقادیر ساده مورد نیاز این شی bean می شود، هسته و قابلیت اصلی یک ظرف است. بنابراین درک چگونگی این روند اهمیت فراوانی دارد. شما تاکنون نمونه هایی از انواع اصلی تزریق وابستگی، یعنی تزریق سازنده و تزریق قراردهنده را دیده اید و می دانید که اسپرینگ از هر دو روش حمایت می کند. شما همچنین می دانید که اسپرینگ چگونه به جای استفاده از یک سازنده برای تهیه یک نمونه bean، می تواند از یک متد کارخانه استفاده کند. از نظر تهیه وابستگی های مورد نیاز bean، استفاده از متد کارخانه برای تهیه یک نمونه bean، معادل تهیه این نمونه از طریق یک سازنده است. ظرف در صورت استفاده از سازنده، آرگومان های (اختیاری) را که همان وابستگی ها هستند، به یک سازنده می دهد. در مورد استفاده از متد کارخانه، ظرف این آرگومان ها را به متد کارخانه می دهد. بنابراین چه این نمونه از طریق متد سازنده مقداردهی گردد و چه متد کارخانه این کار را انجام دهد، از این نقطه نظر برای آن تفاوتی وجود ندارد.

استفاده از تزریق سازنده و تزریق قراردهنده در یک انحصار متقابل نیست. اگر اسپرینگ یک نمونه اولیه از bean را از طریق متد سازنده و یا متد کارخانه بدست آورد، و مقادیر آرگومان را به متد سازنده و یا کارخانه بفرستد، با این کار برخی از وابستگی های این شی را تزریق کرده است، اما هنوز می تواند از تزریق قراردهنده استفاده کرده و وابستگی های بیشتری را تهیه کند. این کار هنگامی مفید است که برای مثال، شما نیاز به استفاده و مقداردهی اولیه یک شی از کلاسی داشته باشید که سازنده ای دارد که یک یا چند آرگومان را از این طریق دریافت و یک bean معتبر ایجاد و برای اعضاء داده ای اختیاری آن، به متدهای قراردهنده این کلاس تکیه می کند. اگر نیاز باشد برخی از اعضاء داده ای اختیاری شی مقداردهی شود، بدون استفاده از هر دو روش تزریق این شی به درستی مقداردهی اولیه نخواهد شد.

اجازه دهید به بررسی چگونگی مقداردهی اولیه و تهیه وابستگی های مورد نیاز یک bean توسط ظرف بپردازیم:

  • ظرف در زمان شروع به کار خود، تعریف bean را بدون نمونه سازی از روی آن مقداردهی اولیه می کند. وابستگی های bean می توانند به صراحت در قالب آرگومان های سازنده، متد کارخانه و/یا اعضاء داده ای bean بیان شده باشند.
  • در یک تعریف bean، هر خصیصه و یا آرگومان سازنده می تواند یک مقدار واقعی بوده که باید تنظیم شود، و یا یک اشاره گر به یکی دیگر از اشیاء bean در کارخانه bean و یا کارخانه والد آن باشد.
  • ظرف تا جایی که بتواند، در زمان مقداردهی اولیه تعریف bean اعتبار سنجی را انجام می دهد. در هنگام استفاده از فرمت XML برای پیکربندی، اگر این پیکربندی از دیدگاه XML DTD معتبر نباشد، یک استثنا در پارسر XML ایجاد می شود. حتی در صورت معتبر بودن XML از لحاظ DTD، اگر اسپرینگ بتواند تشخیص دهد که از دیدگاه منطقی، یکی از تعاریف معتبر نیست، یک استثنا ایجاد خواهد کرد. برای مثال ممکن است دو خصیصه در یک انحصار متقابل باشند، با این وجود راهی برای DTD وجود ندارد که بخواهد آن را بیان کند.
  • اگر یکی از وابستگی های bean پیدا نشود (به این معنی که این وابستگی، bean دیگری است که در واقع وجود ندارد)، و یا یک آرگومان سازنده یا مقدار یک خصیصه به درستی تنظیم نشده باشد، زمانی یک خطا ایجاد می شود که ظرف بخواهد یک نمونه از آن bean را بسازد و وابستگی های آن نمونه را تزریق کند. اگر نمونه ای از bean مورد نیاز نباشد، بنابراین این امکان بالقوه وجود دارد که تعریف bean، شامل خطا (از نوع وابستگی) بوده و تا کنون (تا زمانی که bean استفاده شود) پیدا نشده باشد. به منظور یافتن هرچه زودتر خطاهای احتمالی، به طور پیش فرض بسترهای برنامه (و نه کارخانه bean) در ابتدای کار، یک نمونه از bean را به صورت یگانه ایجاد می کند. به این مرحله، مرحله از پیش نمونه سازی[۳] گفته می شود. مرحله از پیش نمونه سازی، شامل بررسی تمام اشیاء bean به صورت یگانه (حالت پیش فرض) می شود تا از این طریق یک نمونه از هر یک از اشیاء bean که در تزریق وابستگی شرکت دارند ایجاد و ذخیره گردد. مرحله از پیش نمونه سازی را می توان از طریق استفاده از خصیصه default-lazy-init عنصر سطح بالای beans سربارگذاری، و یا به کمک خصیصه lazy-init عنصر bean، کنترل کرد.
  • زمانی که ظرف به یک نمونه جدید یک bean نیاز داشته باشد، به عنوان مثال با فراخوانی ()getBean باید آن را تهیه کند و یا bean دیگری به عنوان یک وابستگی به این bean اشاره کند، از طریق متد سازنده یا متد کارخانه یک نمونه اولیه از این bean ایجاد شده و سپس از طریق آرگومان های اختیاری سازنده یا متد کارخانه، و همچنین مقادیر خصیصه ها، شروع به تزریق وابستگی ها می کند.
  • اگر آرگومان های سازنده و یا اعضاء داده ای bean، به bean دیگری اشاره کنند، ظرف مجبور خواهد شد ابتدا یک نمونه از آن bean را ایجاد کرده و یا به دست آورد. به شی bean که در شی دیگری به آن اشاره شده است، وابستگی bean گفته می شود. این روند می تواند تا زمانی که گراف وابستگی حل نشده باشد ادامه یافته و زنجیره ای از اشیاء bean را ایجاد کند.
  • هر آرگومان سازنده یا مقدار تعیین شده برای یک خصیصه، باید بتواند از نوعی که دارد به نوع واقعی و مورد انتظار آن پارامتر یا خصیصه تبدیل شود. البته این به شرطی است که این دو نوع یکسان نباشند. اسپرینگ قادر است آرگمان هایی که در قالب رشته تهیه شده اند را به تمام انواع عددی، مانند int، long، boolean، و همچنین به انواع پوشش[۴] ها مانند Integer، Long، Boolean و غیره تبدیل کند. همچنین اسپرینگ از مولفه PropertyEditor متعلق به JavaBeans استفاده کرده و هر نوع مقادیر رشته ای را به نوع مورد نظر تبدیل می کند. تعدادی PropertyEditor پیش ساخته به صورت خودکار ثبت و توسط ظرف استفاده می شوند. به عنوان مثال می توان به ClassEditor اشاره کرد که یک رشته نام کلاس را دریافت کرده و آن را به یک شی واقعی از آن کلاس تبدیل می کند، و آن را به عنوان نتیجه کار خود برگرداند، و یا PropertyEditor دیگری به نام ResourceEditor که یک رشته مسیر یک مکان را به یک نوع منبع اسپرینگ[۵] تبدیل می کند. این PropertyEditor برای دسترسی به منابع در حالت انتزاعی استفاده می شود.
  • انواع پیکربندی XML استفاده شده توسط بسیاری از کارخانه های bean و پیاده سازی های بستر برنامه، شامال عناصر/صفات مناسبی هستند که به شما اجازه می دهند مقادیر پیچیده ای را که از نوع کلکسیون های مختلف، مانند List، Set، Map و Property هستند، تعریف کنید. این کلکسیون ها می توانند بنا به نیاز در مکان های مختلف پیکره بندی استفاده شود.
  • همچنین وابستگی ها می توانند ضمنی باشند، به گونه ای که حتی اگر اعلان نشوند، اسپرینگ قادر باشد از reflection استفاده کرده و نوع آرگومان هایی که سازنده آن bean می گیرد و یا نوع اعضاء داده ای آن bean را دریابد، و یک لیست از وابستگی های معتبر برای bean بسازد. سپس ظرف می تواند با استفاده از قابلیتی به نام autowiring، این وابستگی ها را بر اساس نوع و یا نام آن ها در جاهایی که نیاز است قرار دهد. در بخش های بعد با autowiring آشنا خواهیم شد.

تعیین وابستگی های bean، بررسی جزئیات

در این قسمت می خواهیم به جزئیات مخصوص اعضاء داده ای و آرگومان های سازنده یک bean در فرمت XML بپردازیم. هر عنصر bean می تواند تعدادی زیرعنصر constructor-arg داشته باشد که آرگومان های سازنده یا متد lookup را مشخص می کنند. همچنین هر عنصر bean می تواند تعداد زیرعنصر property داشته باشد که خصیصه های JavaBean را که باید مقداردهی شوند، مشخص می کنند. آرگومان های سازنده و اعضاء داده ای JavaBean می توانند در صورت نیاز با یکدیگر ترکیب شوند. این ترکیب در مثال زیر دیده می شود. در این مثال یک آرگومان سازنده وجود دارد که به bean دیگری ارجاع داده شده است. همچنین یک خصیصه از نوع int برای تعیین یک مقدار ساده دیده می شود:

 

بررسی DTD مربوط به XML تعریف bean نشان می دهد که برخی از عناصر می توانند درون یک عنصر property و یا constructor-arg قرار بگیرند. هر یک از این عناصر برای تعیین یک مقدار برای آن خصیصه و یا آرگومان سازنده استفاده می شود. این عناصر عبارتند از:

 

 

عنصر ref برای تنظیم مقدار یک خصیصه و یا آرگومان سازنده جهت ارجاع به bean دیگر موجود در این کارخانه و یا کارخانه والد استفاده می شود:

 

 

برای هر عنصر تنها باید یکی از خصیصه های local، bean، و parent استفاده کرد و این خصیصه باید به شناسه bean دیگری اشاره کند. هنگامی که از خصیصه local استفاده می شود، پارسر XML قادر است در زمان تجزیه XML، وجود bean مورد نظر را بررسی کند. هرچند، از آن جایی که این خصیصه از مکانیسم XML IDREF استفاده می کند، بنابراین باید آن bean در همین فایل XML تعریف شده و برای تعیین شناسه در تعریف آن، باید از خصیصه id و نه name استفاده شده باشد. هنگامی که از خصیصه bean استفاده می شود، bean مشخص شده می تواند در XML همین کارخانه و یا فایل های XML دیگری که برای تعریف کارخانه والد استفاده می شوند قرار داشته باشد. در این روش، خود اسپرینگ (و نه پارسر XML) وجود bean را بررسی خواهد کرد. این بررسی تنها زمانی اتفاقی می افتد که آن وابستگی درخواست شود، اما در زمان شروع به کار کارخانه آن بررسی انجام نخواهد شد. خصیصه parent که خیلی کمتر استفاده می شود، مشخص می کند که bean مورد نظر باید از کارخانه والد به این کارخانه ارسال شود. این اتقاق در زمان های کمی رخ می دهد، مانند زمانی که بین نام bean موجود در این کارخانه و bean کارخانه والد تلاقی نام وجود داشته باشد.

عنصر value برای تعیین مقادیر خصیصه ها یا آرگومان های ساده استفاده می شود. همانطور که قبلا ذکر شد، مقدار ارسال شده از حالت رشته ای به نوعی که خصیصه و یا آرگومان سازنده دارد تبدیل می شود. این مقدار می تواند هر یک از انواع پیش ساخته[۶] عددی یا پوششی و یا نوعی باشد که برای آن یک PropertyEditor در ظرف ثبت شده است. برای مثال کد زیر یک خصیصه از نوع رشته ای به نام classname را برابر رشته ch02.sample6.StaticDataWeatherDaoImpl قرار می دهد، اما اگر classname خصیصه ای از نوع java.lang.Class باشد، کارخانه برای تبدیل این رشته به نمونه ای از Class واقعی از ClassEditor استفاده خواهد کرد.

 

 

 

تهیه یک PropertyEditor برای تبدیل رشته به یک نوع سفارشی برای پیکربندی در ظرف کار ساده ای است. به عنوان مثال زمانی را در نظر بگیرید که بخواهیم تاریخ را به صورت رشته وارد کرده و در خصیصه ای از نوع Date قرار دهیم. از آنجایی که تاریخ، بسیار به موقعیت محلی حساس است، از یک PropertyEditor استفاده کرده و رشته را با فرمت خاص مورد انتظار آن تعریف می کنیم. این ساده ترین راه برای تبدیل این نوع است. در بخش های بعد در مورد روش تهیه یک PropertyEditor سفارشی بیان شده است. اگر PropertyEditor و کلاسی که قرار است تبدیل به آن صورت پذیرد در در یک بسته[۷] قرار داشته باشند و نام PropertyEditor با Editor به پایان برسد، در این صورت این PropertyEditor به صورت خودکار تشخیص داده خواهد شد. به عنوان مثال PropertyEditor با نام MyTypeEditor در بسته کلاس MyType، به طور خودکار تشخیص داده می شود و بدون نیاز به این که به اسپرینگ اطلاع داده شود، توسط کد JavaBeans در کتابخانه های جاوا استفاده می شود.

از آنجایی که یک عنصر value خالی به عنوان یک رشته خالی در نظر گرفته می شود، اعضاء داده ای و یا آرگومان های سازنده ای که باید به مقدار null تنظیم شوند به رویکرد خاصی نیاز دارند. برای این کار از عنصر خاص null استفاده می کنیم:

 

 

هنگامی که نام یک bean را تحت یک مقدار رشته ای تعیین می کنیم، بهتر است برای گرفتن خطا از عنصر idref استفاده کنیم. تعدادی bean کمکی خاص در چارچوب اسپرینگ وجود دارند که به عنوان مقدار یک خصیصه، نام bean دیگری را گرفته و کارهای خاصی را روی آن انجام می دهند. برای قراردهی به این خصیصه ها از روش زیر استفاده می شود:

 

 

هدف عنصر idref این است که اشتباهات را بگیرد و در نتیجه می تواند بسیار مفید باشد. با استفاده از روش زیر، پارسر XML اعتبار سنجی را انجام داده و متوجه ارجاع هایی شود که به یک bean ناموجود اشاره می کند. نتیجه خصیصه value دقیقا همان مقداری خواهد بود که مستقیما از تگ value استفاده شود.

 

 

عناصر list، set، map و props اجازه تعریف و تنظیم اعضاء داده ای و یا آرگومان های سازنده پیچیده از نوع java.util.List، java.util.Set، java.util.Map و java.util.Properties را می دهد. در مثال انتزاعی زیر، یک JavaBean از یک List، Set، Map و Properties استفاده می کند:

 

 

 

مقادیر List، Map و Set می توانند هر یک از عناصر زیر باشند:

 

 

همانطور که در این مثال برای لیست نشان داده شده است، انواع کلکسیون ها می توانند در سطوح مختلف استفاده شوند. نکته ای که باید به خاطر داشته باشید این است که اعضاء داده ای و یا آرگومان هایی که انواع کلکسیون را دریافت می کنند، باید انواع عمومی java.util.List، java.util.Set یا java.util.Map را داشته باشد. شما نمی توانید از انواع کلکسیونی مخصوص اسپرینگ، مانند ArrayList استفاده کنید. همچنین نمی توان یک خصیصه را که دارای نوع عمومی است، در کلاسی قرار گیرد که یک نوع خاص را می پذیرد. یک راه حل این است که از ListFactoryBean، SetFactoryBean و MapFactoryBean – کلکسیونی از اشیاء bean کارخانه[۸] کمکی موجود در اسپرینگ – استفاده کرد. این اعضای کمکی اجازه می دهند نوع کلکسیون مورد استفاده، مثلا java.util.LinkedList را تعیین کنید. در مورد اشیاء bean کارخانه در ادامه توضیح داده شده است.

آخرین عنصری که می تواند به عنوان یک مقدار خصیصه، آرگومان سازنده، و یا در داخل یکی از عناصر کلکسیون استفاده شود، عنصر bean است. این به این معنی است که تعریف bean می تواند به عنوان یک خصیصه، داخل تعریف bean دیگری قرار گیرد. در مثال زیر، تزریق قراردهنده مثال بالا به شکلی دیگر دیده می شود:

 

 

در زمانی که در خارج از محدوده bean بیرونی، هیچ استفاده ای از bean داخلی وجود ندارد، تعاریف bean تودرتو بسیار مفید است. در مثال بالا، DAO سرویس آب و هوا، که به عنوان یک وابستگی برای سرویس آب و هوا در نظر گرفته شده، به صورت یک bean درونی تعریف شده است. هیچ bean و یا کاربرد خارجی دیگری برای DAO آب و هوا وجود ندارد و بنابراین دلیلی برای نگه داشتن آن، در خارج از محدوده bean بیرونی وجود ندارد. استفاده از فرم bean داخلی، باعث ایجاد اختصار و شفافیت بیشتر می شود. اگر چه استفاده از شناسه در bean داخلی مجاز است، اما نیازی به آن نیست. توجه داشته باشید که اشیاء bean داخلی همیشه از نوع غیریگانه هستند و مقدار singleton آن ها، در صورت وجود نادیده گرفته می شود.

اعلام وابستگی ها به صورت دستی

هنگامی که یک خصیصه موجود در یک bean و یا یک آرگومان سازنده، به bean دیگری اشاره می کند، یک اعلام وابستگی به bean دیگر ایجاد شده است. گاهی اوقات نیاز است با وجود این که دو bean، به عنوان خصیصه هم دیگر تعریف نشده اند، یک bean قبل از bean دیگری ساخته و مقداردهی اولیه شود. زمانی که یک کلاس یک عضو ایستا داشته باشد، در هنگام بارگذاری آن باید این عضو مقداردهی اولیه شود و بنابراین ممکن است نیاز باشد این مقداردهی به ایجاد یک نمونه از bean دیگر نیاز داشته باشد. به عنوان مثال، معمولا درایورهای پایگاه داده، خود را در JDBC DriverManager ثبت می کنند. از صفت depends-on برای تعیین وابستگی دستی یک bean به bean دیگر استفاده می شود. در این صورت زمانی که اولین بار به این bean دسترسی پیدا می کنیم، یک نمونه از bean دیگر نیز ایجاد می شود. در مثال زیر چگونگی بارگذاری یک درایور پایگاه داده نشان داده شده است:

 

 

توجه داشته باشید که بسیاری از مجموعه اتصال های پایگاه داده[۹] و کلاس های پشتیبانی اسپرینگ، مانند DriverManagerDataSource باعث می شود این نوع بارگذاری اتفاق بیافتد.

وابستگی های Autowiring

ما تاکنون تعریف صریح وابستگی های bean را، از طرق مقدار یک خصیصه و آرگومان های سازنده انجام داده ایم. اسپرینگ در شرایط زیادی قادر است درون کلاس های bean موجود در کارخانه را بررسی کرده و وابستگی ها را به صورت autowiring رفع کند. در روش autowiring، خصیصه های bean و یا آرگومان های سازنده، در فایل XML به صورت اعلام نشده[۱۰] باقی مانده، اسپرینگ با استفاده از reflection، نوع و نام آن خصیصه را یافته و سپس آن را بر اساس نوع یا نام، به bean دیگر موجود در کارخانه مطابقت می دهد. این کار به طور بالقوه می تواند مقدار قابل توجهی از تعداد دستوراتی که باید نوشته شوند را کاهش دهد، ولی در مقابل مقداری از شفافیت کد کاسته می شود. شما می توانید autowiring را در هر دو سطح کل ظرف و تعریف یک bean کنترل کنید. از آنجا که ممکن است در اثر دقت کم در استفاده از autowiring، آثار نامشخصی به وجود آید، به طور پیش فرض امکان استفاده از autowiring غیر فعال است. Autowiring در سطح bean از طریق خصیصه autowire انجام می شود، که می تواند یکی از پنج مقدار زیر را به خود بگیرد:

  • no: برای این bean هیچ autowire انجام نخواهد شد. اعضاء داده ای و آرگومان های سازنده bean باید به صراحت اعلام شوند، و هرگونه ارجاع به bean دیگر باید از طریق عنصر ref صورت پذیرد. این مقدار در صورت عدم تغییر حالت پیش فرض آن در سطح کارخانه، مقدار پیش فرض سطح اشیاء bean در نظر گرفته می شود. در اکثر شرایط، به ویژه در زمانی که استقرار[۱۱] کمی گسترده تر باشد، توصیه می شود از این حالت استفاده شود.
  • byName: Autowire با استفاده از نام خصیصه. برای پیدا کردن اشیاء bean در کارخانه از نام خصیصه برای انطباق استفاده می شود. اگر نام یک خصیصه weatherDao باشد، در این صورت ظرف سعی می کند در این خصیصه، ارجاعی به bean دیگر با نام weatherDao قرار دهد. اگر bean منطبق با این نام یافت نشد، در این صورت آن خصیصه تنظیم نشده باقی خواهد ماند. در این روش، خصیصه هایی که منطبق نشده باشند، به صورت اختیاری در نظر گرفته می شوند. اگر شما بخواهید در صورت عدم انطباق یک عضو داده ای خطایی ایجاد شود، باید صفت dependency-check = “objects” را به تعریف bean اضافه کنید.
  • byType: Autowire با تطابق نوع انجام می شود. اگر برای هر خصیصه دقیقا یک bean از همان نوع در کارخانه وجود داشته باشد، مقدار خصیصه برابر همان bean قرار خواهد گرفت. در صورتی که بیش از یک bean در کارخانه مطابق با نوع خصیصه وجود داشته باشد، موجب خطا شده و یک استثنا ایجاد می شود. همانند autowire به روش byName، اگر bean منطبق وجود نداشته باشد، در این صورت خصیصه تنظیم نشده باقی می ماند. در این مورد اگر نیاز باشد خطایی تولید شود، باید در تعریف bean صفت dependency-check = ”objects” قرار گیرد.
  • constructor: Autowire از طریق سازنده و بر اساس نوع انجام می شود. این روش برای اعضاء داده ای کاملا مشابه روش byType کار می کند، به جز این که برای هر آرگومان سازنده، باید دقیقا یک تطابق bean بر اساس نوع در کارخانه وجود داشته باشد. در صورتی که چند سازنده وجود داشته باشد، اسپرینگ سعی می کند حریصانه عمل کرده و سازنده ای را انتخاب کند که بیشترین آرگومان آن تطبیق یابد.
  • autodetect: این روش از بین byType یا constructor، آن را که مناسب تر است، انتخاب می کند. bean مورد نظر بررسی شده و اگر سازنده ی پیش فرض بدون آرگومانی داشته باشد، رویکرد byType انتخاب شده و در غیر این صورت، از روش سازنده استفاده می شود.

به کمک خصیصه default-autowire عنصر سطح بالای beans این امکان وجود دارد که حالت پیش فرض autowire (حالت no) را برای همه اشیاء bean موجود در کارخانه تغییر دهیم. برای هر bean می توان از autowiring و ایجاد اتصال صریح، به صورت همزمان استفاده کرد، اما اولویت عناصر property یا constructor-arg برای هر خصیصه بالاتر است.

اجازه دهید نحوه کار autowiring را برای سرویس آب و هوا و DAO بررسی کنیم. همانطور که می بینید، ما تعریف خصیصه weatherDao را در تعریف bean حذف و در عوض autowiring بر اساس نام را فعال کرده ایم، و در نتیجه اسپرینگ هنوز می تواند خصیصه را بر اساس مطابقت نام با bean انطباق دهد. همچنین ما می توانستیم autowiring را بر اساس نوع استفاده کنیم، زیرا این خصیصه از نوع WeatherDao بوده و تنها همین bean در ظرف است که با این نوع مطابقت دارد:

 

استفاده از autowiring برای رهایی از تایپ تنظیمات کارخانه ممکن است وسوسه کننده باشد، ولی از این خصیصه باید با دقت بیشتری استفاده کرد.

نکته مهم: حذف وابستگی هایی که به صراحت اعلام شده اند باعث حذف قسمتی از اسناد مربوط به وابستگی ها می شود. علاوه بر این، هنگام استفاده از byType یا byName، در صورت وجود بیش از یک bean و یا عدم وجود bean منطبق، احتمال رفتار غیرقابل پیش بینی وجود دارد. توصیه می شود برای استقرارهای بزرگتر و پیچیده تر، از autowiring استفاده نشده و یا به دقت استفاده شود، در غیر این صورت ممکن است حتی با وجود این که از حجم XML کاسته شده، باعث افزایش پیچیدگی شود. در حال حاضر اکثر IDE ها از DTD پشتیبانی کرده و دارای ویرایشگر XML پیش ساخته هستند که می تواند در هنگام ایجاد تنظیمات bean به میزان زیادی از تایپ کردن بکاهد و در نتیجه ایجاد وابستگی صریح نگرانی زیادی نخواهد داشت. در ارتباطات سطح پایین مانند DataSource، استفاده از autowiring می تواند مناسب باشد، اما بهتر است زمان هایی که پیچیدگی بالاتر است از ایجاد ارتباط صریح استفاده شود. این روش منجر به کاهش ابهام خواهد شد.

انطباق آرگومان های سازنده

به طور کلی، همیشه بهتر است از صفات اختیاری index و type همراه با عناصر constructor-arg استفاده شود. با توجه به این که این خصیصه ها اختیاری هستند، بدون استفاده از یکی از این دو صفت، لیست آرگومان های سازنده مشخص شده، بر اساس تطبیق نوع به آرگومان های واقعی متصل خواهد شد. هنگامی که قرار است آرگومان ها به انواع مختلف bean و یا انواعی مانند Map اشاره کنند و به خصوص زمانی که تنها یک سازنده وجود دارد، انجام این انطباق برای ظرف کار آسانی است. اما زمانی که چند آرگومان از یک نوع وجود داشته باشند و یا از تگ value استفاده شود، که می تواند در شکل رشته ای خود یک مقدار بی نوع در نظر گرفته شود، تکیه بر تطبیق خودکار می تواند خطاها یا نتایج غیر منتظره ای تولید کند.

یک bean فرضی را در نظر بگیرید که دارای یک سازنده است که یک عدد را به عنوان کد و یک رشته را به عنوان رشته خطا دریافت می کند. اگر ما برای تامین مقادیر این آرگومان ها بخواهیم از تگ <value> استفاده کنیم، نیاز داریم برای انجام درست امور ظرف اطلاعات بیشتری در اختیار آن قرار دهیم. ما می توانیم برای ایجاد تطابق با سازنده واقعی، از صفت index برای تعیین اندیس آرگومان در لیست آرگومان ها استفاده کنیم. اندیس این لیست از صفر شروع می شود:

 

 

 

همچنین ما می توانیم با استفاده از صفت type، برای تعیین نوع یک مقدار، اطلاعات کافی را در اختیار ظرف قرار دهیم تا بتواند تطابق مناسبی را بر اساس نوع انجام دهد:

 

 

اعتبارسنجی وابستگی های bean

در اغلب اوقات، تعدادی از اعضاء داده ای JavaBean یک شی اختیاری هستند. در این مورد زمانی که نیاز باشد، باید این اعضا را مقداردهی کرد. در این صورت راه ساده ای برای ظرف وجود ندارد که در گرفتن خطاهایی که به دلیل عدم وجود مقدار برای خصیصه ای که باید تنظیم می شد، اما تنظیم نشده است، به شما کمک کند. با این حال، هنگامی که یک bean وجود داشته باشد که باید همه اعضاء داده ای آن، و یا اعضاء داده ای نوع خاص آن تنظیم شوند، ویژگی اعتبارسنجی وابستگی ظرف می تواند در این کار کمک کند. هنگامی که این ویژگی فعال باشد، اگر اعضاء داده ای، توسط تعریف صریح و یا از طریق autowiring مقداردهی نشوند، از نظر ظرف یک خطا به وجود آمده است. به طور پیش فرض، ظرف تلاش نمی کند که تعیین مقدار تمام وابستگی ها را بررسی کند، اما شما می توانید این رفتار را با صفت dependency-ckeck در تعریف bean فعال کنید. این صفت می تواند یکی از موارد زیر را به خود بگیرد:

  • none: اعتبارسنجی وابستگی صورت نمی پذیرد. اگر برای خصیصه ای مقداری مشخص نشده باشد، به عنوان خطا تلقی نخواهد شد. مقدار none، مقدار پیش فرض سطح کارخانه bean است و در صورتی که تغییر نکند، رفتار پیش فرض هر یک از اشیاء bean نیز در نظر گرفته می شود.
  • simple: بررسی می کند که انواع داده ای ساده[۱۲] و کلکسیون ها[۱۳] مقداردهی شده باشند، و در صورتی که مقداری نگرفته باشند، یک خطا ایجاد می شود. مقدار سایر اعضاء داده ای بررسی نمی شود.
  • objects: اعضاء داده ای که از انواع اولیه و یا کلکسیون نیستند بررسی می شوند، و در صورتی که مقداری نداشته باشند یک خطا ایجاد می شود. سایر اعضاء داده ای بررسی نمی شوند.
  • all: تمامی اعضاء داده ای، از جمله انواع داده ای اولیه، کلکسیون ها و انواع پیچیده بررسی می شوند.

با استفاده از خصیصه default-dependency-check مربوط به تگ سطح بالای beans می توان حالت پیش فرض none را برای تمامی اشیاء bean تغییر داد.

[۱] Underscore

[۲] Static Method

[۳] Pre-instantiation

[۴] Wrapper

[۵] Spring’s Resource Type

[۶] Built-in

[۷] Package

[۸] Factory Bean

[۹] Database Connection Pool

[۱۰] Undeclared

[۱۱] Deployment

[۱۲] Primitive Types

[۱۳] Collections

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *