طبق گفته ی oracle یکی از مهمترین فناوری مهندسی نرم افزار در جاواست که نتیجه ای از پروژه ی Jigsaw بوده و کمک میکنه بهره وری پروژه بیشتر بشه.
پروژه ی ماژولار کردن Java از نسخه ی 7 کلید خورد و نهایتا در نسخه ی 9 اضافه شد.
هدف JPMS رفع دو مشکل بزرگ در جاوا بود classpath ها وmonolithic jdk :
در حالت عادی هنگامی که کتابخونه (Jar file) هایی که در خود Jdk موجود نیستند استفاده میکنیم باید با -classpath هنگام ران کردن برنامه ادرسیش رو بدیم. حالا اگر اشتباهی دو تا لایبری با ورژن های متفاوت ادد کرده باشیم چی؟مشکلی به وجود میاد به اسم Jar Hell
خب حالا مشکل این Jar hell چیه؟وقتی یه متدی مثلا از کتابخونه ی x رو فراخوانی کردیم که در ورژن 1 هست ولی در ورژن 2 حذف شده و هر دو توی classpath ادد شدن یکیشون رندوم استفاده میشن! درواقع هنگام کامپایل هیچ خبری نیست ولی موقع ران شدن توی یکسری شرایط به ارور بر میخورید.
وmonolithic jdk چیه؟ کلاس های جاوا به شدت به هم وابستگی داشتن و وقتی یک برنامه ی ساده که مثلا میخواستیم چیزی رو فقط پرینت کنیم (مثلا hello world) باید کل JRE رو روی سیستممون نصب میکردیم مثلا چیزی حدود 50مگابایت فقط برای hello world و حالا اگر برنامه ی سخت افزاری میخواستیم بنویسیم با محدودیت حافظه هم مواجه میشدیم و...
نتیجه ی ماژولار کردن Java بهبود عملکرد خود Jvm هم هست .JSR 376 (Java Specification Requests)
نشون داده که عملکرد بهینه سازی Jvm وقتی که ماژول ها تفکیک شدن و فقط اونایی که لازم هستن اضافه شدن بهبود پیدا کرده
یکی دیگه از مواردی که با JPMS به ارمغان اومده و درواقع از مهمترین اهداف JPMS بود کپسوله سازی قدرتمند تر هست (Strong encapsulation):
پکیج های یک ماژول فقط در صورتی در اختیار دیگر ماژول ها قرار میگیرن که در ماژول به صراحت اعلام بشه میخواد فلان پکیج رو در اختیار دیگر ماژول ها قرار بده ! تازه اوناهم فقط وقتی میتونن ازش استفاده کنن که به صراحت اعلام کنن بهش نیاز دارن.این حرکت کپسوله سازی رو قوی تر میکنه برای مثال کلاس هایی در خود جاوا هست (internal Api ) که فقط برای APIداخلی JDK طراحی شدن ولی توسط کلاس های خارجی مورداستفاده قرار میگرفتن این قضیه میتونه باعث کاهش امنیت بشه ولی با JPMS این مشکل هم مرتفع شده.
مشاهده ی لیست ماژول های جاوا در سیستم های مختلف :
و اگه بخوایم اطلاعات یک ماژول رو به دست بیاریم :
نحوه ی تعریف module :
ابتدا باید یک فایل به نام module-info.java ایجاد کنید و اون رو در root folder قرار بدید (مثلا پکیج اصلی برنامه هست com.example.myprogram باید خارج از این پکیج و دقیقا کنار پوشه ی com باشه .. درونش نباشه ها فقط توی مسیری باشه که پوشه ی com هست):
فایل باید به صورت بالا شروع باشه با نام ماژول.
کلمات کلیدی رزرو شده در تعریف ماژول هم اینها هستند:
exports, module, open, opens, provides, requires, uses, with, to transitive.
بریم سراغ توضیح کلمات کلیدی بالا :(کد هارو رو درون اکولادی که بالا تعریف کردیم مینویسیم)
همینطور که از اسمش مشخصه داره میگه اقای jvm این ماژول من وابستگی داره به فلان ماژول و تو اددش کن به classpath من.
البته برای ماژول هایی که فقط موقع کامپایل نیازشون داریم از حرکت زیر استفاده میکنیم:
اما خب دقیقا منظورمون از ماژول هایی که فقط زمان کامپایل نیازشون داریم چیه؟ خیلی از کتابخونه ها فقط زمان کامپایل به کار میان و اصلا قرار نیست زمان رانتایم استفاده بشن (مثل Lombok که درواقع بر اساس annotation هایی که زدیم میاد سورس کد رو تولید میکنه نکته ی مهم اینکه تمام انوتیشن هایی که فقط زمان کامپایل باید در دسترس باشن RetentionPolicy شون CLASS یا SOURCE هست ) این ماژول ها برای زمان رانتایم اختیاری هستن.
یه چیز دیگه داریم به اسم :
اینم میاد میگه ای کسایی که دارید از ماژول من استفاده میکنید و ای منی که خودم دارم از یه ماژول دیگه استفاده میکنم که کار شمارو انجام بدم.. پس شما بصورت ضمنی داری از اون ماژول منم استفاده میکنی.
فرض کنید یک ماژول Bar دارید که اون داره از ماژول Drink استفاده میکنه و یه همچین کدی هم در نظر بگیرید :
حالا اگر ماژولی به نام customer که داره از Bar استفاده میکنه بیاد متد buyDrink رو کال کنه خطای کامپایل رخ میده با این منطق که ماژول customer به ماژول Drink دسترسی نداره و باید با استفاده از requiers Drink; توی فایل Module-info خودش مشخص کنه. اما راه حل منطقی تر اینه :مایی که کتابخونه نوشتیم باید تمام این داستانارو گردن بگیریم و بگیم هرکس از ما استفاده کرد درواقع از فلان ماژول هم استفاده کرده. پس توی فایل module-info ای که در ماژول Bar هست مینویسیم:
حالا اگر بخوایم مشخص کنیم ماژول ما فقط برای بعضی ماژولای دیگه قابل مشاهده باشه چی ؟(بهش میگن qualified export) خیلی راحت میایم از exports...to استفاده میکنیم :
حالا اگر بخوایم مشخص کنیم ماژول ما فقط برای بعضی ماژولای دیگه قابل مشاهده باشه چی ؟(بهش میگن qualified export) خیلی راحت میایم از exports...to استفاده میکنیم :
میدونیم که توسط رفلکشن (reflection) اعضای private کلاس و... قابل دسترسیه ولی توی jpms این خبرا نیست باید اجازه بدیم که ماژول دیگری ایا میتونه توسط رفلکشن از ماژول ما استفاده کنه یا نه:
و اگر بخوایم فقط برای یک ماژول خاص باشه مثل exports...to از opens...to استفاده میکنیم.
مثال opens رو میتونیم توی یک پروژه ی جاوا اف ایکسی که داخل fxml اومدیم برای یک ویو fx:id تنظیم کردیم و توی کنترل با انوتیشن @FXML اون رو گرفتیم. این حرکت با reflection انجام میشه و باید حتما ماژولمون برای javafx.fxml چی باشه؟افرین ! opens باشه:
اگر کمی دقت کنید دیگه ما برای کپسوله سازی فقط با private, protected ,public, package طرف نیستیم بلکه با پابلیکی که فقط برای ماژول ما پابلیکه. پابلیکی که برای همه ی ماژول ها پابلیکه و پابلیکی که برای بعضی ماژول ها پابلیک هم طرف هستیم که این دقیقا میشه همون Strong encapsulation
دو مورد دیگه میمونه به نام uses و provides..with که مربوط میشه به ServiceLoader که خواندنش رو به خودتون واگذار میکنم.
در مطلب بعدی اینکه چطور با ماژولاریتی کردن پروژه میتونیم یک JDKسفارشی بسازیم که حجمش فقط بر اساس ماژول های مورد نیاز ما باشه صحبت میکنیم.
منابع :
Openjfx jigsaw
Understanding Java 9 Modules
Javaland