HandlerMapping – Spring MVC

تاکنون با تعریف الگوهای URL در فایل web.xml آشنا شده ایم. تمام آدرس هایی که با این الگوهای تطبیق داده می شوند، به DispatcherServlet هدایت شده و توسط یک یا چند HandlerMapping برداشته و پردازش می شوند. HandlerMapping قادر است مسیر اجرا را از درون برنامه تحت وب تشخیص دهد. همانطور که قبلا دیدیم، زنجیره اجرا، متشکل از یک کنترلر و احتمالا یک یا چند interceptor است.

تعیین مسیر اجرا می تواند با استفاده از معیارهای گوناگون انجام شود. بازرترین راه برای تعیین کنترلر مورد نظر، استفاده از اطلاعات درون URL درخواستی است. هرچند، شما با استفاده از Spring، محدود به استفاده از URL نخواهید بود. اگر شما بخواهید، می توانید از HandlerMapping ساخته خودتان استفاده کنید که می تواند با ارث بری از AbstractHandlerMapping و پیاده سازی getHandlerInternal (HttpServletRequest) و یا پیاده سازی اینترفیس HandlerMapping تهیه شود.

در صورت نیاز، شما می توانید بیش از یک HandlerMapping تعریف کنید. با این کار، تا زمانی که Spring یک مسیر مناسب اجرا پیدا نکند هر یک از HandlerMappingها را بررسی خواهد کرد. HandlerMapping، اینترفیس Orders را پیاده سازی کرده است؛ به این معنی که در هنگام راه اندازی، context، تمام HandlerMappingها را بازرسی و آنها را با استفاده از OrderComparator مرتبط می کند.

اگر پس از پرس و جوی تمام HandlerMappingهای موجود، باز هم HandlerExecutionChain مورد نظر پیدا نشود، Spring، کاربر را با ارسال کد خطای HTTP 404 (عدم وجود صفحه مورد نظر) آگاه خواهد کرد.

توجه داشته باشید که نیازی نیست که در context برنامه، یک HandlerMapping تعریف کنید. اگر Spring در context، چیزی را پیدا نکند، در آن صورت از BeanNameUrlHandlerMapping استفاده خواهد کرد.

Spring، با استفاده از معیارهای معمول برای تعیین مسیر، HandlerMappingهایی را تهیه کرده است. اجازه دهید به بررسی آنها بپردازیم.

BeanNameUrlHandlerMapping

ما به کمک BeanNameUrlHandlerMapping می توانیم با استفاده از نام URLها در context برنامه وب، آنها را به handler مربوطه متصل کنیم. به عنوان مثال، به بخشی از یک context برنامه های تحت وب که در ادامه آورده شده، توجه کنید:

 

کنترلر مشخص شده، برای تمامی درخواست هایی که به هدف account.edit، /secure/largeaccount.edit/ و secure/smallaccount.edit/ ارسال شده باشند پاسخ می دهد.

اگر شما HandlerMapping مناسبی را در context برنامه های تحت وب خود تعریف نکرده باشید، در این صورت از BeanNameUrlHandlerMapping استفاده خواهد شد.

برای استفاده مناسب از BeanNameUrlHandlerMapping، نیاز است با سبک Ant برای تعریف مسیر آشنا شوید. Ant با استفاده از ستاره و علامت سوال می تواند یک مسیر مشترک را مشخص کند. در صورت وجود یک علامت سوال در عبارت مسیر، زمانی url با عبارت مسیر جفت می شوند که با جایگزین کردن یک کاراکتر با علامت سوال، بتوان به url رسید. البته مقدار این کاراکتر اهمیتی ندارد. برای مثال، با توجه به مسیر secure/test.?sp/، هر دو secure/test.jsp/ و secure/test.asp/ مطابقت خواهند داشت.

علاوه بر علامت سوال، شما می توانید از ستاره به عنوان wildcard استفاده کنید، که می تواند جایگزین هر تعداد کاراکتر شده و یا حتی جایگزین هیچ کاراکتری نشود؛ به این معنی که هر دو secure/simpletest.jsp/ و secure/test.jsp/ در ارزیابی secure/*test.jsp/ مطابقت خواهند داشت. استفاده از دو ستاره، این را نشان می دهد که شما می خواهید آدرس ها در تمام مسیرها مطابقت پیدا کنند. به عنوان مثال، در هنگام استفاده از test.jsp/**/ به عنوان یک عبارت مسیر، آدرس های secure/test.jsp، /nonsecure/deeper/test.jsp/ و همچنین test.jsp/ مطابقت خواهند کرد.

SimpleUrlHandlerMapping

علاوه بر BeanNameUrlHandlerMapping، Spring شامل HandlerMapping دیگری است که می تواند در context برنامه قرار گرفته و بر اساس URL درخواستی نگاشت را انجام دهد. شما با استفاده از سبک Ant برای عبارات مسیر، و SimpleUrlHandlerMapping، می توانید ساختار URL پیچیده تری ایجاد کرده و به یک handler نگاشت دهید. این دو ابزار در کنار یکدیگر ویژگی های پیشرفته تری را در اختیار شما می گذارند. نمونه زیر را در نظر بگیرید:

 

قطعه کد بالا یک کنترلر را برای ویرایش حساب ها نشان می دهد. این کنترلر به همه درخواست های ورودی که URL آنها با الگوی (secure/account*.edit/) مطابقت داشته باشد نگاشت خواهد شد.

در مثال قبل، ما با استفاده از خصیصه urlMap، نگاشتی را از URLهای مورد نظر به کنترلر ایجاد کردیم. ما همچنین می توانستیم از خصیصه mapping برای این کار استفاده کنیم. اما در این صورت امکان ارجاع مستقیم به کنترلر وجود نداشت. در صورت استفاده از خصیصه mapping، باید برای نگاشت آدرس ها و یا الگوهای URL به beanها، از شی Properties استفاده کنیم. این امکان به ما اجازه می دهد که برای نگاشت، از فایل دیگری استفاده کنیم. به عنوان مثال به قطع کد زیر توجه کنید:

 

به طور پیش فرض، Spring با همه کنترلرها به صورت singleton برخورد می کند. در برخی شرایط (که در آن به صراحت با استفاده از </ “ref bean=”XXX> به beanها اشاره می شود)، حتی تنظیم خصوصیت singleton برای bean مورد نظر به مقدار false، باعث نمی شود که چند bean ایجاد شود. راه حل این مشکل در صورت استفاده از SimpleUrlHandlerMapping این است که از خصیصه mapping، بدون اشاره مستقیم به bean، و بلکه با تعیین کنترلر با نام آن، استفاده کنیم. هر زمان Spring به کنترلر موجود در mapping نیاز داشته باشد، یک نمونه از آن را از ApplicationContext درخواست می کند (که در این صورت به خصیصه singleton توجه می شود). به عبارت دیگر، اگر شما می خواهید یک کنترلر را به عنوان یک prototype مدل کنید (که البته پیشنهاد نمی شود)، از خصیصه mapping استفاده کنید که از نام beanها در خود استفاده کرده باشد و همچنین مقدار singleton را برای کنترلر مورد نظر، برابر false قرار دهید. این کار باعث می شود تا کنترلر یک بار مصرف – برای مثال، برای زمانی که کنترلر دارای خاصیت  threadsafe نیست – ایجاد شود.

CommonsPathMapUrlHandlerMapping

Spring برای کار با ویژگی های مشترک تعریف شده در کنترلر یک HandlerMapping مناسب، تدارک دیده است.

استفاده از CommonsPathMapUrlHandlerMapping باعث می شود نیاز به استفاده از نگاشتی بین کنترلر و urlها در context برنامه وب نباشد. در عوض شما ویژگی هایی را در کنترلر تعریف می کنید که حاوی اطلاعات مورد نیاز برای نگاشت URLها به آن باشد. استفاده از کامپایلر و نمایه ساز مربوط به ویژگی های مشترک، به Spring این قابلیت را می دهد که به صورت خودکار درون کنترلرها را جستجو کند. استفاده از ویژگی های مشترک، زمانی مناسب است که از کنترلرهای ساده ای درون چند برنامه تحت وب استفاده می کنید و نمی خواهید در context برنامه به آنها اشاره کنید.

به مثال زیر توجه کنید. در ابتدای کار، ما یک کنترلر می نویسیم:

 

 

این کنترل مدلی حاوی چند ژانر آماده کرده و متناسب با آن یک ModelAndView برمی گرداند. همانطور که مشاهده می کنید، ما یک ویژگی مشترک در بخش JavaDoc کنترلر قرار داده ایم که به  یک URL اشاره می کند. در زمان اجرا، Spring از این ویژگی برای نگاشت  URL، که در این مثال showGenres.html/ است، به کنترلر استفاده خواهد کرد. همانند سایر HandlerMappingهای قبل، شما می توانید از سبک Ant برای بیان مسیر استفاده کنید.

علاوه بر این، ما یک Dependency را به کد اضافه کرده ایم که کنترلر برای بازیابی لیست ژانرها، به آن نیاز دارد. کنترلرها با استفاده از ویژگی های مشترک و سایر امکانات متاداده ی در دسترس (مانند متاداده JDK 1.8)، به urlها متصل می شوند و لازم نیست در context برنامه های تحت وب به آنها اشاره شود. آنها توسط Spring و بر اساس این ویژگی ها شناسایی می شوند. از آنجا که اغلب اوقات برای بازیابی اطلاعات، از سرویس های لایه میانی، مانند یک پایگاه داده استفاده می کنیم، هنگامی که Spring سرویس مورد نظر را شناسایی کند، آن را به صورت خودکار به کنترلر متصل می کند. اتصال به صورت خودکار می تواند با استفاده از سازنده و توابع setter انجام می شود. حالت استفاده از setter در مثال قبل نشان داده شده است.

همانطور که می بینید، نیاز است نام کلاس ویژگی مورد نظر (PathMap) به طور کامل در JavaDoc مشخص شود. ما می توانستیم پس از import کردن کلاس PathMap، آن را به صورت PathMap(“/showGenres.html”)@@ استفاده کنیم. با این حال، بسیاری از IDEها، import استفاده نشده را شناسایی و حذف می کنند و تشخیص نمی دهند که ممکن است از آن در ویژگی های مشترک استفاده شده باشد. حذف import استفاده نشده توسط IDE و کامپایل ویژگی PathMap پس از حذف، باعث به وجود آمدن خطای کامپایل خواهد شد. ما توصیه می کنیم از نام کامل کلاس برای ویژگی مشترک مورد نظر استفاده شود، زیرا تعیین خطای به وجود آمده به خاطر حذف importها توسط IDE، کار سختی خواهد بود.

در این مرحله، کنترلر راه اندازی شد، اما هنوز نیاز است دو مورد دیگر انجام شود. اول اینکه در context برنامه، به CommonsPathMapHandlerMapping اشاره شود. این کار بسیار ساده است:

 

تنها مورد باقی مانده، کامپایل ویژگی ها، با استفاده از کامپایلر ویژگی های مشترک و نمایه سازی کلاس هایی است که از این ویژگی ها استفاده کرده اند. این مورد نیز در بخش های بعد توضیح داده خواهد شد.

استفاده از بیش از یک HandlerMapping

Spring، به عنوان یک container پیچیده IoC، اجازه تعریف بیش از یک نمونه از مولفه های خاص را می دهد. به طور خاص، از این امکان برای HandlerMappingها، ViewResolverها و ExceptionResolverها بیشتر استفاده می شود. در مورد HandlerMapping، این قانون طلایی وجود دارد که اگر یک HandlerMapping یک نمونه مناسب از نوع HandlerExecutionChain ارائه نکند (و به عبارت دیگر مقدار null برگرداند) از HandlerMapping بعدی استفاده خواهد شد. تنها زمانی یک استثنا ایجاد می شود که هیچ یک از HandlerMappingها، پاسخ مناسبی تولید نکنند.

اگر بیش از یک HandlerMapping تعریف کنید، Spring بر اساس مرتب سازی حاصل از اینترفیس Ordered، آنها را فراخوانی خواهد کرد. سفارشی سازی ترتیب می تواند با تنظیم خصیصه order، در هر یک از HandlerMappingها صورت پذیرد:

 

این مثال نشان می دهد که چگونه دو HandlerMapping تعریف کنیم که BeanNameUrlHandlerMapping در آن مقدم بر SimpleUrlHandlerMapping باشد. این ترتیب باعث می شود هر کنترلر تعریف شده در context نرم افزار، مستقیما و قبل از کنترلری فراخوانی شود که در نگاشت properties استفاده شده در HandlerMapping دوم مشخص شده است.

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

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