View Full Version : annotation چیست و چطوری میشه annotation های شخصی نوشت
tux-world
جمعه 23 مرداد 1394, 23:21 عصر
سلام دوستان. در مورد این سوال یه کم اگه میشه توضیح بدید و اینکه چطوری میتونم خودم از اونا بنویسم برای کارای خودم :لبخندساده:
ahmad.mo74
شنبه 24 مرداد 1394, 10:04 صبح
http://tutorials.jenkov.com/java/annotations.html
http://www.mkyong.com/java/java-custom-annotations-example/
http://www.javacodegeeks.com/2014/11/java-annotations-tutorial.html
-سیّد-
شنبه 24 مرداد 1394, 10:54 صبح
من یه توضیح کلی و سریع بدم:
Annotation در واقع meta-data ای هست که شما روی کد اضافه میکنید. مثلاً یه تابع تعریف میکنید، میتونید annotate اش کنید که این تابع فلان کار رو میکنه. مثال بسیار کاربردیش توی کتابخونهی hibernate هست، که شما اونجا متغیرهاتون رو annotate میکنید و این کتابخونه به کمک annotation های شما متوجه میشه که چطوری باید کلاسها رو به جداول پایگاه داده map کنه.
نحوهی استفاده ازشون هم به کمک Reflection هست. شما میتونید از یه کلاس یا فیلد یا تابع، فهرست annotation هاش رو بگیرید و پردازش کنید.
اگه annotation نباشه، مجبورید این دادهها رو مثلاً توی یه فایل کانفیگ بذارید، یا توی یه Map قرار بدید. خوبیای که annotation داره اینه که داخل کدتون و دقیقاً در کنار متغیر یا تابع یا کلاس مربوطه هست و این هم باعث میشه که خوانایی کد به شدت بالا بره، و هم باعث میشه چیزی از قلم نیافته (این خیلی معموله که آدم یه چیزی رو یادش بره توی config بذاره، یا توی Map بنویسه).
tux-world
شنبه 24 مرداد 1394, 16:24 عصر
دستتون درد نکنه. یه مثال مفید هم میتونید برای واضح تر شدن قضیه کاربرد اون بزنید؟ ممنون میشم برا اینکه بفهمم واقعا چه کاربرد اساسی و نقش خوبی بازی میکنه
-سیّد-
شنبه 24 مرداد 1394, 22:51 عصر
خوب جا داره من یه مشق به شما بدم!
فرض کنید یه سایت دارید، که توش چندین کار (action) مختلف میشه انجام داد. مثلاً سادهاش میشه این که میشه توش یه کاربر اضافه کرد، فهرست کاربرا رو دید، یه کاربر رو ویرایش کرد، و یه کاربر رو حذف کرد (عملیات CRUD).
پس تا اینجا یه کلاس داریم مثلاً به نام MyController که توش ۴ تا تابع بالا هست (برای سادگی کار، توابع رو static گرفتم):
public class MyController {
public static void createAction() {
...
}
public static void listAction() {
...
}
public static void editAction() {
...
}
public static void deleteAction() {
...
}
}
حالا میخوایم یه تابع route داشته باشیم که action رو به عنوان ورودی بگیره، و action مورد نظر رو انجام بده (تابع متناظرش رو فراخوانی کنه). این کار رو توسط Reflection میشه اینطوری انجام داد:
public void route(String action) throws Exception {
Method m = MyController.class.getDeclaredMethod(action + "Action");
m.invoke(null);
}
(خیلی سادهسازی توی کد انجام دادم (و یه سری از best practice ها رو نقض کردم) که بشه مثال رو راحت جلو برد، لطفاً خیلی توجه نکنید!)
تا اینجای کار همه چیز خوبه!
حالا فرض کنید توی این سایت میخوایم سطوح دسترسی و Access Control تعریف کنیم. نقشهامون هم از قبل مشخص هستند:
admin: به تمام action هایی که گفتم دسترسی داره.
operator: میتونه فهرست کاربرا رو ببینه و هر کاربری رو ویرایش کنه.
guest: فقط میتونه فهرست کاربرا رو ببینه.
حالا میخوایم این سطوح دسترسی رو توی کد تابع route اعمال کنیم و اگه یکی با سطح دسترسی نامناسب اومد سراغ یه تابع، یه Exception خاص (مثلاً AccessDeniedException) پرتاب کنیم. یعنی تعریف تابع route به این صورت تغییر میکنه:
public void route(String action, String role) throws Expection {
...
}
خوب بسمالله! لطفاً این تابع رو پیادهسازی کنید و در جواب این پست بنویسید. (لطفاً دوستانی که جواب نهایی رو بلدن دخالت نکنن! پیشاپیش ممنونم!)
tux-world
یک شنبه 25 مرداد 1394, 00:35 صبح
ممنون. درست نفهمیدم. برای تابع آخری یعنی یه annonate تعریف کنم؟ یا بر اساس اون role بگم که روت چطور کار کنه؟:لبخندساده:
-سیّد-
یک شنبه 25 مرداد 1394, 06:04 صبح
نه شما فعلاً annotation رو بیخیال شو! معمولی بنویس.
tux-world
یک شنبه 25 مرداد 1394, 08:32 صبح
خوب راستش تو این موارد جاوا زیاد نیستم. الان چیزی که به ذهنم میرسه این هستش که ما روی role یه سوییچ بندازیم. یا میشه سطوح دسترسی رو بزاریم داخل آرایه و از
Arrays.asList(array).contains(value)
استفاده کنیم ببینیم چی هستش ولی این خوب بدترین حالت ممکنی هستش که به این شکل میشه ازش استفاده کرد. از جاوا من تو اندروید استفاده میکنم برای همین دیدی به این قضیه ندارم. سرچ هم کردم ولی چیزی پیدا نکردم. ببخشید
-سیّد-
یک شنبه 25 مرداد 1394, 19:07 عصر
بسیار عالی! من دنبال همین بودم (همیشه نباید مشق رو یه جوری طرح کرد که طرف مقابل بتونه کامل حلش کنه! :چشمک: ). البته توی نسخههای قبلی جاوا، روی String نمیشد switch گذاشت، و مجبور بودیم با یه مشت if-else حلش کنیم.
ولی روش دومتون خوبه. من یه کم دقیقترش میکنم:
میتونیم برای هر action، یه آرایه داشته باشیم از نقشهایی که بهش دسترسی دارن، و بعد بررسی کنیم که آیا نقش مورد نظر توی آرایهی مربوط به action مورد نظر موجود هست یا نه (البته به طور کلی برای بررسی کردن موجود بودن یه المان توی یه فهرست، بهتره از HashSet به جای آرایه یا List استفاده کنیم که با سرعت بالا جوابمون رو میده). این Map رو هم به صورت static یه بار میسازیم که هر دفعه بتونیم توش نگاه کنیم. پس میشه اینطوری:
...
private static final Map<String, Set<String>> ACL = new HashMap<String, Set<String>>();
static {
ACL.put("create", new HashSet<String>(Arrays.asList("admin")));
ACL.put("list", new HashSet<String>(Arrays.asList("admin", "operator", "guest")));
ACL.put("edit", new HashSet<String>(Arrays.asList("admin", "operator")));
ACL.put("delete", new HashSet<String>(Arrays.asList("admin")));
}
public void route(String action, String role) throws Expection {
if (!ACL.get(action).contains(role))
throw new AccessDeniedException();
Method m = MyController.class.getDeclaredMethod(action + "Action");
m.invoke(null);
}
این روش به خوبی جواب میده و ما خوشحالیم! :لبخند:
حالا فرض کنید کد شما بزرگ و بزرگتر شد، و از ۴ تا دونه action هی گسترشش دادید، تا این که شد ۱۰۰ تا action! چی کار باید بکنیم؟ باید دونه به دونه هر action ای که اضافه میکنیم، حواسمون باشه که بیایم اینجا هم یه مدخل توی map مون اضافه کنیم. این کار چند تا اشکال داره. یکی این که به خاطر این که این ۲ تا کار از هم مجزا هستن، یعنی action رو یه جا تعریف میکنیم و یه جای دیگه باید بیایم کنترل دسترسیش رو اضافه کنیم، کاملاً ممکنه که یادمون بره سطح دسترسی رو اضافه کنیم. یه اشکال دیگهاش اینه که اینجا یه کدی داریم که هی بزرگتر و بزرگتر میشه! شما اگه مطابق MVC کد بزنید، action های مختلف رو توی controller های مختلف مینویسید، ولی دسترسیها همه توی این یه کلاس تجمیع میشه و کد به شددددددت ناخوانا میشه (تصور کنید از بین صد خط کد دسترسی (که همه شبیه هم هستن!)، بخواین یکی رو ویرایش کنید!).
یه اشکال بزرگ دیگه که این روش داره، اینه که اینطوری نمیتونید یه کتابخونه بسازید که عملیات کنترل دسترسی رو انجام بده. یعنی فرض کنید میخواین این روش route کردن رو به صورت کتابخونه در بیارید، و بشه ازش توی جاهای دیگه هم استفاده کرد. یعنی من نفر دوم بتونم با وابستگی به کتابخونهی شما، یه controller جدید با action های custom خودم رو بنویسم و روش بتونم سطوح دسترسی رو کنترل کنم. برای این کار باید به متغیر ACL که اینجا تعریف کردید دسترسی داشته باشم. اگه این متغیر رو public کنید که من بهش دسترسی داشته باشم (یا یه متد add به صورت public static تعریف کنید که بتونم به کمک اون دسترسیهای مورد نظر خودم رو اضافه کنم)، مشکلی که پیش میاد اینه که میتونم بقیهی سطوح دسترسی رو هم دستکاری کنم. یعنی مثلاً میتونم این Map رو clear کنم و همهی دسترسیها بپره!
برای حل این ۲ مشکل، میتونیم از annotation استفاده کنیم. چه جوری؟ یه Annotation تعریف میکنیم به نام AccessControl، که توش اسم role مورد نظر هست:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface AccessControl {
String[] value() default "guest";
}
اولین خط (Retention) برای این هست که به JVM بگیم اطلاعات مربوط به این Annotation رو موقع اجرای برنامه حفظ کنه، که بشه بازیابیشون کرد (اگه این رو نگیم، پیشفرضش RetentionPolicy.CLASS هست که یعنی موقع اجرا نمیشه اطلاعات مربوط به این Annotation رو بازیابی کرد).
خط دوم یعنی این Annotation رو میشه روی method ها به کار برد، نه کلاسها و پارامترها و غیره.
بعد هم که تعریف خود Annotation هست، که یه المان به صورت آرایهای از String داره (که نقشها توشن).
خوب حالا بیایم ازش استفاده کنیم. روی هر کدوم از action هایی که تعریف کردیم، یه دونه از این Annotation ها میذاریم:
public class MyController {
@AccessControl("admin")
public static void createAction() {
...
}
@AccessControl({"admin", "operator", "guest"})
public static void listAction() {
...
}
@AccessControl({"admin", "operator"})
public static void editAction() {
...
}
@AccessControl("admin")
public static void deleteAction() {
...
}
}
میبینید چقدر قشنگ و تمیز شد؟ هر کسی action تعریف میکنه، همونجا هم کنارش دسترسیهاش رو میگه (و کسی نمیتونه دسترسیهای کس دیگه رو عوض کنه).
دقت کنید که وقتی نوع رو آرایه تعریف میکنید، میتونید مقدار رو یه دونه بدید، یا آرایه بدید.
حالا بیاین توی تابع route مون بخونیمش و ازش استفاده کنیم:
public void route(String action, String role) throws Expection {
Method m = MyController.class.getDeclaredMethod(action + "Action")
AccessControl ac = m.getDeclaredAnnotation(AccessControl.class);
if (ac == null)
throw new ActionNotAnnotatedException();
if (!Arrays.asList(ac.value()).contains(role))
throw new AccessDeniedException();
m.invoke(null);
}
اینجا چند تا نکته هست:
یکی این که وقتی میگیم m.getDeclaredAnnotation و نوع مورد نظر رو بهش میدیم، در صورتی که روی تابع annotation ای از نوع مورد نظر ما تعریف نشده باشه، null برمیگردونه، که در این صورت داریم یه exception از نوع ActionNotAnnotatedException پرتاب میکنیم. پس باز هم ممکنه یه نفر یادش بره روی action اش annotation ما رو بزنه، ولی احتمالش کمتره.
دوم این که مقدار annotation مورد نظر رو که میخونیم، یه آرایه هست از نقشها. بنابراین باید توش بگردیم ببینیم نقش مورد نظرمون توش هست یا نه. البته این کار یه مقدار هزینه داره (چون دونه به دونه توی خونههای آرایه رو میگرده)، ولی برای مثال ما کافیه.
در نهایت هم بگم که برای پیادهسازی Access Control معمولاً از این روش استفاده نمیکنن. اگه بخواین کنترل دسترسی کاملاً پویا داشته باشید، بهترین راه اینه که از پایگاه داده استفاده کنید. ما برای مثال از Annotation این کارو کردیم.
نکتهی دیگه این که معمولاً توی یه پروژه، زیاد با تعریف کردن Annotation سر و کار نداریم و بیشتر ازشون استفاده میکنیم. یعنی Annotation رو برای کاربردهای یه مقدار خاص تعریف میکنن و بعیده توی یه پروژهی معمولی نیاز بشه که شما برای خودتون یه Annotation تعریف کنید. به عنوان مثال، توی کل بخشهای جاوایی موتور جستجوی یوز (که خیلی هم زیاد هستند)، من یک جا رو یادم میاد که دوستان برای یه کار خاص Annotation تعریف کرده بودن (دقت کنید! از Annotation زیاد استفاده میشه! ولی Annotation جدید زیاد تعریف نمیشه).
نکته در مورد Annotation ها زیاده (مثلاً این که اسم المان رو بذاریم value، یا مقدار پیشفرضشون، یا این که توی جاوا ۸ مفهوم Repeated Annotation اضافه شده، ...)، نکات بیشتر رو به منابعی که دوستان معرفی کردن مراجعه کنید.
tux-world
دوشنبه 26 مرداد 1394, 01:11 صبح
دستت درد نکنه سید الان خیلی خوب فهمیدم قضیه چیه. خدا خیرت بده برادر. یه جاهایی انگار خودم برای کارای کوچیکم میتونم استفاده کنم ازشون. ممنون :لبخندساده:
vBulletin® v4.2.5, Copyright ©2000-1404, Jelsoft Enterprises Ltd.