در این بخش به مبحث Spring Bean Annotation میپردازیم که برای تعریف کردن انواع bean ها در Spring استفاده میشوند
سه حالت تعریف یک bean در Spring وجود دارد :
- تعریف کردن آنها در فایل کانفیگ XML پروژه
- با استفاده از Bean@ در کلاس ها
- با استفاده از annotation های موجود در پکیج org.springframework.stereotype
ComponentScan@ اسکن کردن Component ها :
اگر از Spring Boot استفاده کنیم از پکیج اصلی برنامه خودش Component ها را اسکن میکند ولی اگر Spring Boot استفاده نکنیم باید از ComponentScan@ استفاده کنیم و در Spring ما Component scanning را فعال کنیم، و بعد Spring میتواند بصورت اتوماتیک کلیه bean ها را اسکن و شناسایی کند.
با کمک ComponentScan@ میتوانیم base package ای که bean ها در آنها جای گرفته اند را معرفی کنیم اگر نام package را وارد نکنیم زمان بالا آمدن Spring Container را طولانی تر کرده ایم و اسکن Bean ها از کلاسی که ComponentScan@ قرار گرفته در نظر گرفته میشود
@Configuration @ComponentScan(basePackages = "com.baeldung.annotations") class VehicleFactoryConfig {}
همچنین میتوانیم بجای basePackage از کلاسی که basePackage از آنجا آغاز میشود را به عنوان آرگومان بدهیم :
@Configuration @ComponentScan(basePackageClasses = VehicleFactoryConfig.class) class VehicleFactoryConfig {}
هر دوی این آرگومان های میتوانند بصورت آرایه ای از basePackage یا basePackageClasses ورودی بگیرند. همچنین با توجه به ویژگی جاوا 8 میتوانیم از این annotation بیش از یکبار برای وارد کردن چندین آدرس استفاده کنیم :
@Configuration @ComponentScan(basePackages = "com.baeldung.annotations") @ComponentScan(basePackageClasses = VehicleFactoryConfig.class) class VehicleFactoryConfig {}
و یا به صورت آرایه داخلی آنرا پیاده کنیم :
@Configuration @ComponentScans({ @ComponentScan(basePackages = "com.baeldung.annotations"), @ComponentScan(basePackageClasses = VehicleFactoryConfig.class) }) class VehicleFactoryConfig {}
ذر صورت استفاده از فایل کانفیگ xml به این صورت میتوان آنرا تعریف کرد :
<context:component-scan base-package="com.baeldung" />
Component@ : روی کلاس های component قرار میگیرد و Spring بصورت اتوماتیک آنها را شناسایی خواهد کرد و بصورت پیش فرض نام bean شان همان نام کلاس با حروف کوچک است ولی میتوانیم با آرگومان value نام دیگری به آن انتصاب بدهیم
@Component class CarUtility { // ... }
Repository@ : از این annotation برای تعریف bean هایی برای DAO یا Repository Class استفاده میشود که وظیفه آنها لایه دسترسی به دیتابیس است
@Repository class VehicleRepository { // ... }
یکی از مزایای استفاده از این annotation فعال کردن اتوماتیک انتقال خطای های عملیات دیتابیس است که از ORM های مورد استفاده مانند Hibernate ارسال میشوند و برای فعال سازی نیاز به داشتن bean دیگری برای اینکار داریم :
@Bean public PersistenceExceptionTranslationPostProcessor exceptionTranslation() { return new PersistenceExceptionTranslationPostProcessor(); }
توجه داشته باشید که در اغلب موارد Spring بصورت اتوماتیک bean بالا را ایجاد خواهد کرد
و در صورت استفاده از تنظیمات xml میتوانیم تگ زیر را اضافه کنیم :
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
Service@ : منطق برنامه معمولا در کلاس های service نوشته میشوند ما برای ایجاد این لایه از این annotation استفاده میکنیم
@Service public class VehicleService { // ... }
Controller@ : این annotation به Spring میگوید که این کلاس سرویس یک کلاس Controller در Spring MVC است
@Controller public class VehicleController { // ... }
Configuration@ : در کلاسی که این annotation قرار دارد ما میتوانیم یکسری Bean را با استفاده از Bean@ بصورت برگشتی از متد تعریف کنیم
@Configuration class VehicleFactoryConfig { @Bean Engine engine() { return new Engine(); } }
گروه Annotaion های Stereotype ها و نقش انها در AOP :
برای راحت کردن کار با AOP در Spring یکسری annotation برای مشخص کردن pointcut ها.
فرض کنید میخواهیم زمان اجرای عملیات دیتابیس را در لایه DAO اندازه بگیریم، با استفاده از Annotaion های AspectJ زیر :
@Aspect @Component public class PerformanceAspect { @Pointcut("within(@org.springframework.stereotype.Repository *)") public void repositoryClassMethods() {}; @Around("repositoryClassMethods()") public Object measureMethodExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.nanoTime(); Object returnValue = joinPoint.proceed(); long end = System.nanoTime(); String methodName = joinPoint.getSignature().getName(); System.out.println( "Execution of " + methodName + " took " + TimeUnit.NANOSECONDS.toMillis(end - start) + " ms"); return returnValue; } }
در این کد ابتدا یک pointcut ایجاد کرده ایم که با تمامی متد های موجود در کلاسی که Repository@ است مطابقت دارد و بعد با استفاده از Around@ زمان اجرا متد ها را اندازه گیری کرده ایم
با استفاده از این روش میتوانیم logger , performance management و ... را پیاده سازی کنیم
فرق بین Componenet@ و Repository@ و Service@ :
در اغلب برنامه های متداول لایه های متفاوتی مثل Data Access, Presentation, Service, Business Logic و ... را داریم و طبیعتا برای هر لایه ما bean های مختلفی داریم که بعد از اسکن شدن در ApplicationContext ثبت میشوند و برای هر نوع از این bean ها از annotation مخصوص همان طبقه بندی استفاده میکنیم و اینطوری خوانایی کد را نیز بالاتر میبریم و باعث میشود کد از یک اصول منطقی پیروی کند ممکن است در نگاه اول این سه annotation مشابه هم به نظر بیایند و کارکردی مشابه داشته باشند، حالا به بررسی بیشتر این سه annotation میپردازیم :
Component@ :
ما از این annotation در کل پروژه میتوانیم برای مشخص کردن کلاس های مدیریت کامپوننت ها استفاده کنیم. Spring تنها bean های داخل کلاس Component@ را داخل Application Context ثبت میکند و اگر bean ای داخل کلاس های Repository@ و Service@ بود آنها قابل شناسایی و ثبت نیستند. اگر داخل اینترفیس های Service و Repository که annotation های ما را میسازند نگاه کنیم خواهیم دید که این اینترفیس ها خود دارای Component@ هستند و به نوعی Component هستند ولی در Spring این کلاس ها کارکرد متفاوتی از Component خواهند داشت
@Component public @interface Service { }
@Component public @interface Repository { }
Repository@ :
کار اصلی یک Repository Bean گرفتن خطاهای خاص عملیات دیتابیس است که خارج از Spring اتفاق افتاده (مثلا در ORM) را به محیط Spring منتقل کند و دوباره آنها را پرتاب کند برای مدیریت و کارکرد صحیح آن همیشه باید یک bean تحت نام PersistenceExceptionTranslationPostProcessor در Application Context مان داشته باشیم که در بالا به آن پرداختیم
Service@ :
این دسته از Bean ها نقش Business Logic را بر عهده دارند و هیچ annotation دیگری برای مشخص کردن business logic در Spring تعریف نشده است
خیلی ممنون از آموزشتون 🙏🌹