از نگارش JavaEE 6.0 به بعد جهت اهداف مختلف و سبکهای متنوع برنامه نویسی کلاسهای Bean مختلفی تعریف و ایجاد شده است. که اغلب برنامه نویسان و طراحان وب آگاهی کافی در رابطه با این نوع کلاسها نداشته و نمیدانند چه زمانی باید از آنها استفاده نمایند. در این مقاله سعی خواهیم کرد تفاوت های آنها را با هم بیان نموده و در آخر تجربیات شخصی خود را در رابطه با این نوع از کلاسهای Bean برای شما بازگو خواهم کرد.

کتابخانه JSF بصورت پایه همراه با کلاسهای Bean از نوع ManagedBean طراحی شده و تکنولوژی جدیدتر CDI (Context Dependency Inject) از نگارش JSF 2.0 به بعد به هسته مرکزی JSF اضافه شده است. CDI را می توان جایگزینی جهت کلاسهای نوع EJB در نظر گرفت. البته نظر شخصی من در این رابطه با این مفهوم تفاوت دارد. بهتر است قبل از هرچیزی به بیان تفاوت های و ویژگی های این نوع کلاسها بپردازیم.

کلاس JSF Managed Bean

اگر کمی در سایتهای مختلف جستجو کرده و تفاوتهای این نوع کلاسها را بررسی کنید، خواهید دید که اکثر نویسندگان گفته اند که اگر در برنامه خود قصد دارید از قابلیتهای JavaEE بصورت کامل استفاده کنید، از این نوع کلاس (JSF Managed Bean) استفاده نکنید. نویسندگان این مقالات هیچ توضیحی به علت ضعف این نوع کلاس نداده و صرفا اشاره کرده اند که نسبت به CDI Bean از قابلیتهای کمتری برخوردار میباشد.

یکی از تفاوتهای اصلی JSF Managed Bean و CDI Bean اینست که مدیریت کلاسهای JSF Managed Bean بر عهده کتابخانه JSF بوده در صورتی که مدیریت کلاسهای CDI Bean را Application Server بر عهده دارد.این مفهوم یعنی اینکه ایجاد و حذف کلاسهای CDI Bean را Application Server بصورت اتوماتیک انجام میدهد.

جهت تعریف کلاسهای JSF Managed Bean باید از علامت @ManagedBean از پکیج javax.faces.bean استفاده نمائید. این نوع کلاسها را می توان از در Scope های مختلف JSF از قبیل @RequestScoped، @SessionScoped ، @ApplicationScoped و @ViewScope استفاده نمود.

علامت @ManagedBean دارای یک Attribute اختیاری به نام name بوده که با استفاده از آن می توان نام پیش فرض کلاس را برای استفاده در صفحه JSF تغییر داد. در این زیر نحوه ایجاد یک نمونه از این نوع کلاس نشان داده شده است.

@ManagedBean

@RequestScoped

Public class TestBean{

  …………

}

یکی از قابیلتهای مهم JSF Managed Bean که آنرا از CDI Bean و EJB متمایز میسازد، استفاده ی آن در ناحیه @ViewScoped میباشد. @ViewScoped یکی از Scope های حافظه تعریف شده در JSF 2.0 بوده که جهت استفاده صفحاتی که از تکنولوژی Ajax استفاده میکنند، ایجاد شده است.

بهتره است این ویژگی JSF Managed Bean با یک مثال ساده توضیح دهیم.

فرض کنید کلاس TestBean در ناحیه @ViewScoped ایجاد شده و دارای متدی بنام helloWorld() بوده که باید در یک صفحه JSF مرتبط با آن به عنوان مثال index.xhtml فراخوانی گردد. در این حالت هنگامی که کاربر صفحه index.xhtml را باز میکند، یک نمونه از کلاس Managed Bean ایجاد میگردد. حال تا زمانی که کاربر در همین صفحه باقی مانده و صفحه ی JSF تمامی درخواستهای کاربر را با استفاده از AJAX اجرا نماید(به عنوان مثال حذف یک آیتم و یا اضافه کردن آن، و ... )، هیچ نمونه ی(Instance) جدیدی از این کلاس ایجاد نمیگردد.

اگر این کلاس در ناحیه @RequestScoped تعریف گردد، با اجرای هر دستور کاربر، یک نمونه جدید از کلاس ایجاد شده و نمونه های قبلی از بین رفته، که این خود باعث میگردد نتوانید به مقادیر ذخیره شده در کلاس دسترسی داشته باشید. به عنوان مثال اگر بعد از ذخیره یک آیتم در پایگاه داده کد کالای ایجاد شده را در یک فیلد از کلاس ذخیره می کنید، و میخواهید که کد کالا را در همان صفحه به کاربر نمایش دهید، با توجه به اینکه بعد از اجرای دستور ذخیره، نمونه کلاس ایجاد شده در حافظه، از بین رفته است، اینکار عملا امکانپذیر نخواهد بود.

 

استفاده از @RequestScope برای صفحاتی که از تکنولوژی Ajax استفاده نمیکنند، و یا صفحات تک کاره(فقط Insert، یا Update و ...) بسیار مفید میباشد.

 

اگر این کلاس در ناحیه @SessionScoped و یا @ApplicationScoped تعریف گردد، Instance کلاس ایجاد شده، تا زمانی که کاربر مرورگر را نبندد در حافظه سرور باقیمانده و موجب افزایش بار بر روی سرور و هدر رفتن منابع آن میشود.

 

ناحیه @ViewScoped برای CDI Bean و EJB تا این زمان در نگارش های مختلف JSF 2.1 قابل استفاده نبوده و شاید در نگارشهای بعد(JSF 2.2) اضافه گردد.البته ناحیه جدیدی بنام @ConversationScoped برای CDI Bean تعریف شده که میتوان آنرا به عنوان جایگزینی برای @ViewScoped معرفی نمود. ناحیه @ConversationScoped یک ناحیه ی سبکتر از @SessionScoped میباشد. خود من قبلا از این نوع ناحیه وکلاسهای CDI Bean در برنامه ها استفاده میکردم، اما چون مدیریت این نوع ناحیه بخصوص حذف و ایجاد نمونه های مختلف آن دردسر بسیار داشت، کلا برنامه ها را به ناحیه @ViewScoped انتقال داده و تا الان هم هیچ مشکلی با این نوع Bean نداشته ام. مثلا یکی از مشکلات من در رابطه با @ConversationScoped این بوده که چون این نوع ناحیه مشتقی از @SessionScoped میباشد، تا زمانی که کاربر در مرورگر در حال تعامل بوده، تمامی اطلاعات رد و بدل شده حافظه باقی می ماند، که برای رفع این مشکل میبایست کلاسها را در رویداد UnLoad صفحه بصورت دسترسی از حافظه حذف میکردم.

اگر بخواهید یک کلاس Managed Bean را در کلاس Managed Bean دیگر استفاده نمائید باید از علامت @ManagedProperty استفاده نمائید.

 

کلاس CDI Bean

CDI Bean های یکی از قابلیتهای جدیدی است که در نگارش JavaEE 6 به بعد اضافه شده است. تعریف یک کلاس CDI Bean شبیه کلاسهای Managed Bean بوده با این تفاوت که در باید از علامت @Named استفاده گردد. در زیر نحوه ایجاد این نوع از کلاسهای Bean نشان داده شده است :

@Named

@RequestScoped

Public void TestBean {

    @Inject

    Private SomeService someService;

     ……

}

علامت @Named در پکیج javax.enterprise.context قرار داشته که برای استفاده از آن باید این پکیج را در قسمتهای کدهای برنامه import نمائید. در این نوع از کلاسهای Bean می توان از ناحیه های @SessionScoped، @RequestScoped، @ConversationScoped و @ApplicationScoped استفاده نمود. توجه داشته باشید که این Annotation ها باید از پکیج javax.enterprise.context فراخوانی گردد(پکیچ  javax.faces.bean مخصوص استفاده Managed Bean میباشد).

 

اگر در پروژه های JSF خود از CDI Bean استفاده میکنید، میبایست یک فایل خالی بنام beans.xml را در داخل دایرکتوری WEB-INF برنامه خود قرار دهید.  Application Server با استفاده از این فایل مدیریت کلاسهای CDI را انجام میدهد.

 

یکی از قابلیتهای CDI اینست که میتوان یک کلاس CDI دیگر را با تمام داده ها در داخل دیگر کلاسها با استفاده از علامت @Inject فراخوانی نمود. این علامت در پکیج javax.inject.Inject قرارداده شده است. توجه داشته باشید که با فراخوانی کلاسهای CDI Bean با استفاده از @Inject دیگر نیازی به استفاده از کلمه new برای ایجاد کلاس نبوده و اینکار بصورت اتوماتیک ایجاد میگردد.(حذف کلاسهای Inject شده نیز بصورت اتوماتیک انجام میگردد).

 

بهترین ناحیه حافطه برای کلاسهای CDI Bean ناحیه @ConversationScoped میباشد.

کلاس های EJB

EJB را می توان یک نگارش قدیمی تر از CDI با مقداری تفاوت بیان نمود. مهمترین تفاوتهای CDI Bean و EJB را می توان بصورت زیر خلاصه نمود:

  • Transaction های اتوماتیک
  • استفاده از EJB بصورت Remote یا Local
  • تعریف بصورت Stateful
  • استفاده از آن جهت ایجاد Timer در برنامه
  • Asynchronous نمودن کلاسها

در JavaEE دو نوع کلاس EJB را میتوان تعریف نمود : Stateless و Stateful. طول عمر کلاسهای EJB از نوع Stateless پایدار نبوده و تنها در طول یک درخواست Web ذخیره و نگهداری میشود، با درخواست جدید نمونه ایجاد شده از این کلاس حذف میگردد.

 

اما طول عمر کلاس EJB از نوع Stateful زیاد بوده و تا زمان Dispose شدن کلاس در حافظه سرور باقی خواهد ماند. تعریف یک کلاس EJB بسیار ساده و باید تنها یکی از علامتهای @Stateles و یا @Stateful را در بالای عنوان کلاس خود بصورت زیر اضافه نمائید :

@Stateless

public class BookingService {

   public String makeReservation(Item Item,Customer customer) {

        ....

        ....

   }

}

نکته : طول عمر کلاسهای Stateless وابستگی بسیاری به Scope تعریف شده دارد، ناحیه پیش فرض برای کلاسهای EJB، @RequestScoped میباشد.

 

اگر میخواهید یک کلاس EJB در صفحه JSF مورد استفاده قرار گیرد باید از علامت @Named استفاده نمائید.(من خودم تا حالا از این روش استفاده نکردم.)

 

نکات و خلاصه مطالب:

  • اگر Transaction در برنامه های شما نقش حیاتی داره، بهترین گزینه کلاسهای EJB میباشد.
  • اگر از ناحیه @ViewScoped استفاده میکنید، کلاس های Bean حتما باید بصورت Serializable تعریف گردد.
  • صفحاتی که در آنها در Ajax استفاده میکنید، بهتر است در ناحیه @ViewScoped تعریف گردند.
  • CDI Bean در سرورهای Servlet مانند Tomcat قابل استفاده نمیباشد.
  • بعضی از Application Server ها مانند Weblogic پشتیبانی کاملی از CDI Bean ها ندارند.
  • اگر هیچ ناحیه ی حافظه ای برای کلاسهای خود تعریف نکنید ناحیه @RequestScoped در نظر گرفته میشود.

 

من خودم در همه برنامه های از Managed Bean و EJB استفاده میکنم.