نمطي في جافا 9

كان الابتكار الرئيسي في Java 9 هو إدخال النمطية. كان هناك الكثير من الحديث عن هذه الميزة ، تم تأجيل تاريخ الإصدار عدة مرات لإنهاء كل شيء بشكل صحيح. في هذا المنشور ، سنتحدث عن ما يمنح آلية الوحدات ، وماذا أفادت Java 9 بشكل عام. كان أساس المنشور هو تقرير زميلي سيرجي مالكيفيتش .




لتنفيذ الوحدات في هذا الإصدار من Java ، تم تخصيص مشروع كامل - Project Jigsaw - والذي يتضمن العديد من JEP و JSR.



لمحبي التوثيق الرسمي ، يمكنك قراءة المزيد عن كل JEP هنا .

المزيد عن Project Jigsaw


بدأ تطوير مشروع Jigsaw ، الذي يطبق الوحدات النمطية ، في عام 2005: تم إصدار أول JSR 277 ، وبالفعل في عام 2008 ، بدأ العمل المباشر في المشروع. تم إصدار الإصدار فقط في عام 2017. أي ، من أجل ربط الوحدات النمطية في Java ، استغرق الأمر ما يقرب من 10 سنوات. والذي ، في الواقع ، يؤكد على النطاق الكامل للعمل والتغييرات التي تم إجراؤها أثناء تنفيذ الوحدات النمطية.

ما هي الأهداف التي وضعها المطورون:

  • تسهيل تطوير التطبيقات والمكتبات الكبيرة ؛
  • تحسين أمان Java SE بشكل عام و JDK بشكل خاص ؛
  • زيادة أداء التطبيق ؛
  • إنشاء القدرة على تقليل حجم JRE للتشغيل على الأجهزة الصغيرة حتى لا تستهلك الكثير من الذاكرة ؛
  • JAR HELL (المزيد عن ذلك لاحقًا).

ما جلبت مفيدة جافا 9


قبل الإصدار 9 ، كانت JDK و JRE متجانسة. نما حجمها مع كل إصدار. احتلت Java 8 بالفعل مئات الميغابايتات ، وكان على المطورين "حملها معهم" في كل مرة حتى يتمكنوا من تشغيل تطبيقات Java. فقط rt.jar وحده يزن حوالي 60 ميجا بايت. حسنًا ، هنا نضيف أيضًا بداية بطيئة واستهلاكًا عاليًا للذاكرة. جاوة 9 جاءت للإنقاذ

في JDK 9تم إدخال فصل الوحدة النمطية ، وهي تم تقسيم JDK إلى 73 وحدة. ومع كل إصدار جديد ، يتزايد عدد هذه الوحدات. في الإصدار 11 ، هذا الرقم قريب من 100. سمح هذا الفصل للمطورين بإنشاء الأداة المساعدة JLINK. باستخدام JLINK ، يمكنك إنشاء مجموعات JRE مخصصة تتضمن فقط الوحدات "الضرورية" التي يحتاجها تطبيقك حقًا. وبالتالي ، فإن تطبيقًا بسيطًا وبعض customJRE مع مجموعة صغيرة (أو صغيرة) من الوحدات يمكن أن يصل حجمها في النهاية إلى 20 ميجا بايت ، وهو خبر جيد.

يمكن العثور على قائمة الوحدات هنا .

مع ظهور Java 9 ، تغيرت بنية JDK: وهي الآن مطابقة لبنية JRE. إذا كان JDK يشتمل في وقت سابق على مجلد JRE ، حيث يوجد bin مرة أخرى ويتم نسخ الملفات ، فكل شيء يبدو الآن كما يلي:



الوحدات


فعلا. ما هي الوحدة؟ الوحدة النمطية هي مستوى جديد من تجميع الحزمة والموارد (أصلها. "مجموعة فريدة من الحزم ذات الصلة قابلة لإعادة الاستخدام ومسمعة بشكل فريد ، بالإضافة إلى الموارد وموصف الوحدة النمطية" ).

يتم تسليم الوحدات النمطية في ملفات JAR مع الحزم ووحدة وصف الوحدة
النمطية- info.java . يحتوي ملف module-info.java على وصف للوحدة النمطية:
الاسم ، التبعيات ، الحزم المصدرة ، الخدمات المستهلكة والمقدمة ، أذونات الوصول عبر الانعكاس.

أمثلة على أوصاف واصف الوحدة:

module java.sql {
    requires transitive java.logging;
    requires transitive java.transaction.xa;
    requires transitive java.xml;

    exports java.sql;
    exports javax.sql;

    uses java.sql.Driver;
}

module jdk.javadoc {
   requires java.xml;
   
   requires transitive java.compiler;
   requires transitive jdk.compiler;
   
   exports jdk.javadoc.doclet;
   
   provides java.util.spi.ToolProvider with
       jdk.javadoc.internal.tool.JavadocToolProvider;
   
   provides javax.tools.DocumentationTool with
       jdk.javadoc.internal.api.JavadocTool;
   
   provides javax.tools.Tool with
      jdk.javadoc.internal.api.JavadocTool;   
}

بعد وحدة الكلمات الرئيسية ، لدينا اسم حزمة jdk.javadoc ، والتي تعتمد على حزمة java.xml أخرى وتعتمد على حزم أخرى.

دعونا نلقي نظرة فاحصة على كل من الكلمات الرئيسية:

  • يتطلب يشير إلى الوحدات التي تعتمد عليها الوحدة الحالية ؛

  • يتطلب متعدية - اعتماد متعدٍ - يعني ما يلي: إذا كانت الوحدة m1 تعتمد بشكل عابر على الوحدة النمطية m2 ، ولدينا وحدة ثالثة mX ، والتي تعتمد على m1 - ستتمكن الوحدة mX أيضًا من الوصول إلى m2 ؛

  • يتطلب ثابت يسمح لك بتحديد تبعيات وقت الترجمة ؛

  • exports , ( “”);

  • exports...to… : export com.my.package.name to com.specific.package; - () () ;

  • uses , :

    uses java.sql.Driver;

    , ;

  • provides , :

    provides javax.tools.Tool with
        jdk.javadoc.internal.api.JavadocTool;

    javax.tools.Tool, with — .

نبذة عن الخدمات:

لنفترض أن لدينا العديد من الوحدات المتصلة التي تنفذ خدمة مجردة - MyService . عند إنشاء التطبيق ، لدينا الفرصة لتحديد تنفيذ الخدمة الذي سيتم استخدامه من خلال "سحب" وحدات تنفيذ الخدمة المطلوبة إلى --module-path :

Iterable<MyService> services = 
        ServiceLoader.load(MyService.class);

وبالتالي ، يحتوي Iterator الذي تم إرجاعه على قائمة بتطبيقات واجهة MyService. في الواقع ، سيحتوي على جميع التطبيقات الموجودة في الوحدات الموجودة على - مسار الوحدة النمطية .

لماذا ، من حيث المبدأ ، تم تقديم الخدمات؟ إنها مطلوبة لإظهار كيفية استخدام الكود الخاص بنا. أي أن هناك دور دلالية. أيضًا ، تتعلق الوحدات النمطية بالتغليف والأمان ، حيث يمكننا جعل التنفيذ خاصًا واستبعاد إمكانية الوصول غير المصرح به من خلال التفكير.

أيضًا ، أحد الخيارات لاستخدام الخدمات هو تنفيذ الإضافات البسيطة إلى حد ما. يمكننا تنفيذ واجهة البرنامج المساعد لتطبيقنا وربط الوحدات للعمل معها.

دعونا نعود إلى بناء الجملة لوصف الوحدات:

حتى 9ki ، من خلال التأمل ، كان بإمكاننا الوصول إلى كل شيء تقريبًا ويمكننا فعل ما نريد وبكل ما نريد. والإصدار التاسع ، كما سبق ذكره ، يسمح لك بحماية نفسك من الوصول إلى التفكير "غير القانوني".

يمكننا فتح الوحدة تمامًا للوصول إلى الانعكاس من خلال الإعلان عن فتح :

open module my.module {
}

أو يمكننا تحديد أي حزم للوصول إلى التفكير من خلال التصريح بفتح :

module my.module {
    opens com.my.coolpackage;
}

هنا من الممكن أيضا لاستخدام يفتح com.my.coolpackage إلى ... ، وبالتالي إعطاء الوصول انعكاس ل com.my.coolpackage حزمة من حزمة أننا سوف تبين بعد ل .

أنواع الوحدات


يصنف Project Jigsaw الوحدات على النحو التالي:

  • System Modules — Java SE JDK . , java --list-modules.

  • Application Modules — , , ( ), .

  • Automatic Modules — , Java JAR-. , , - . JAR- --module-path Java , JAR-.

  • Unnamed Module — , JAR-, --class-path. Java .

Class-path vs module-path


مع ظهور الوحدات ، ظهر مفهوم جديد - مسار الوحدة . في الجوهر ، هذا هو نفس المسار الطبقي ، ولكن بالنسبة للوحدات النمطية.

يتم إطلاق تطبيق معياري كما يلي:



في وضع التشغيل العادي ، نحدد الخيارات والمسار الكامل للفئة الرئيسية. في حالة رغبتنا في العمل مع الوحدات ، فإننا نحدد أيضًا الخيارات والمعلمة -m أو -module ، والتي تشير فقط إلى أننا سنقوم بتشغيل الوحدات. أي أننا نترجم تطبيقنا تلقائيًا إلى وضع معياري. بعد ذلك ، نشير إلى اسم الوحدة والمسار إلى الفئة الرئيسية من الوحدة.

أيضا، إذا كان في الوضع العادي تعودنا على العمل مع -cp و خيارات --class مسار، في الوضع المعياري ، نصف معلمة جديدة -p و --module-path ، والتي تشير إلى المسارات إلى الوحدات المستخدمة في التطبيق.

غالبًا ما ألتقي بحقيقة أن المطورين لا يتحولون إلى الإصدار 9+ ، لأنهم يعتقدون أنه سيتعين عليهم العمل مع الوحدات. على الرغم من أنه في الواقع ، يمكننا تشغيل تطبيقاتنا في الوضع القديم ، ببساطة بدون كتابة معلمة أو استخدام الوحدات النمطية ، ولكن باستخدام رقائق جديدة فقط.

جرة الجحيم


أريد أيضًا أن أتطرق قطريًا إلى مشكلة Jar Hell.



ما هو جار الجحيم باختصار؟ على سبيل المثال، لدينا نوع من التطبيق لدينا وذلك يعتمد على وX مكتبة و مكتبة Y . في الوقت نفسه ، تعتمد كلتا المكتبتين على مكتبة Z ، ولكن على إصدارات مختلفة: X يعتمد على الإصدار 1 ، ص على الإصدار 2 . حسنًا ، إذا كان الإصدار 2 متوافقًا مع الإصدارات السابقة ، فلا مشكلة. وإذا لم يكن الأمر كذلك ، فمن الواضح أننا نحصل على تعارض في الإصدار ، أي أنه لا يمكن تحميل نفس المكتبة في الذاكرة بواسطة محمل الفصل نفسه.

كيف تخرج من هذا الموقف؟ هناك طرق قياسية استخدمها المطورون منذ جافا الأولى ، على سبيل المثال ، استبعاد شخص ما يستخدم مكونات إضافية لـ Maven ، والتي تعيد تسمية أسماء الحزم الجذر للمكتبة. أو يبحث المطورون عن إصدارات مختلفة من مكتبة X للعثور على خيار متوافق.

لماذا أنا: كانت أول نماذج Jigsaw تشير إلى أن الوحدة تحتوي على إصدار وسمحت بتحميل عدة إصدارات من خلال ClassLoaders مختلفة ، ولكن تم التخلي عنها لاحقًا. نتيجة لذلك ، "الرصاصة الفضية" ، التي كان ينتظرها الكثيرون ، لم تنجح.

ولكن ، بمجرد إخراجنا من العلبة ، كنا في أمان قليلاً من هذه المشاكل. جافا 9 يعطل الحزم المجزأة- الحزم المقسمة إلى عدة وحدات. أي أنه إذا كان لدينا الحزمة com.my.coolpackage في وحدة واحدة ، فلا يمكننا استخدامها في وحدة أخرى داخل نفس التطبيق. عندما تبدأ التطبيق بوحدات تحتوي على نفس الحزم ، فإننا ببساطة نتعطل. يلغي هذا التحسين الصغير إمكانية حدوث سلوك غير متوقع فيما يتعلق بتنزيل حزم Split.

أيضًا ، بالإضافة إلى الوحدات نفسها ، هناك أيضًا آلية طبقة أو طبقات Jigsaw ، والتي تساعد أيضًا على التعامل مع مشكلة Jar Hell.

يمكن تعريف طبقة الصور المقطوعة على أنها نظام معياري محلي. وهنا تجدر الإشارة إلى أن حزم Split المذكورة أعلاه ممنوعة فقط في إطار طبقة Jigsaw واحدة. الوحدات التي لها نفس الحزم لها مكان ، ولكن يجب أن تنتمي إلى طبقات مختلفة.

يبدو كما يلي:



عندما يبدأ التطبيق ، يتم إنشاء طبقة التمهيد ، والتي تتضمن وحدات النظام الأساسي التي تم تحميلها بواسطة Bootstrap ، ووحدات النظام الأساسي الإضافية التي تم تحميلها بواسطة محمل النظام الأساسي ووحدات التطبيق الخاصة بنا التي تم تحميلها بواسطة أداة تحميل التطبيق.

في أي وقت ، يمكننا إنشاء طبقات خاصة بنا و "وضع" وحدات من إصدارات مختلفة هناك وعدم سقوطها.

هناك حديث رائع ومفصل على YouTube حول الموضوع: Escaping Jar Hell مع Jigsaw Layers

استنتاج


يفتح محرك الوحدة النمطية من Java 9 إمكانيات جديدة لنا ، في حين أن دعم المكتبات اليوم صغير جدًا. نعم ، يدير الناس Spring ، Spring Boot وهكذا. لكن معظم المكتبات لم تتحول إلى الاستخدام الكامل للوحدات. لذلك ، على ما يبدو ، تم إدراك كل هذه التغييرات بشكل متشكك من قبل المجتمع التقني. توفر لنا الوحدات الدراسية فرصًا جديدة ، لكن مسألة الطلب لا تزال مفتوحة.

وأخيرًا ، أقدم مجموعة مختارة من المواد حول هذا الموضوع: ملخص

مشروع Jigsaw

JDK

Paul Deitel - فهم وحدات Java 9 وحدات

baeldung.com - مقدمة لمشروع Jigsaw

Alex Buckley - Modular Development with JDK 9

Evgeny Kozlov - الوحدات النمطية في Java

All Articles