برنامج Java 9 التعليمي لأولئك الذين يجب عليهم العمل مع التعليمات البرمجية القديمة

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

كل بضع سنوات ، مع إصدار نسخة جديدة من Java ، يبدأ المتحدثون في JavaOne بالاستمتاع ببنيات اللغة الجديدة وواجهات برمجة التطبيقات ، والثناء على فضائلهم. وفي الوقت نفسه ، يتطلع المطورون المتحمسون إلى إدخال ميزات جديدة. هذه الصورة بعيدة عن الواقع - فهي لا تأخذ في الاعتبار تمامًا أن معظم المبرمجين مشغولون في دعم وإنهاء التطبيقات الحالية ، ولا يكتبون تطبيقات جديدة من البداية.

يجب أن تكون معظم التطبيقات - خاصةً التطبيقات التجارية - متوافقة مع الإصدارات السابقة من Java التي لا تدعم كل هذه الميزات الجديدة. أخيرًا ، يشعر معظم العملاء والمستخدمين النهائيين ، خاصة في قطاع المؤسسات الكبيرة ، بالقلق من الترقية الجذرية لمنصة جافا ، ويفضلون الانتظار حتى يصبح أقوى.

لذلك ، بمجرد أن يحاول المطور تجربة جديدة ، يواجه مشاكل. هل ستستخدم طرق الواجهة الافتراضية في التعليمات البرمجية الخاصة بك؟ ربما - إذا كنت محظوظًا ولا يحتاج تطبيقك إلى التفاعل مع Java 7 أو أقل. هل تريد استخدام فئة java.util.concurrent.ThreadLocalRandom لإنشاء أرقام عشوائية زائفة في تطبيق متعدد الخيوط؟ لن يعمل إذا كان تطبيقك يعمل على Java 6 أو 7 أو 8 أو 9 في نفس الوقت.

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

لذا ، هل هناك أي شيء في الإصدار الجديد من Java 9 للمبرمجين المشاركين في دعم التعليمات البرمجية القديمة؟ شيء يمكن أن يجعل حياتهم أسهل؟ لحسن الحظ ، نعم.

ما كان يجب القيام به بدعم الكود القديم هو ظهور Java 9

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

على سبيل المثال ، يمكنك تطبيق الربط المتأخر إذا كنت تريد الوصول إلى واجهة برمجة التطبيقات الجديدة عندما يحتاج تطبيقك أيضًا إلى العمل مع الإصدارات القديمة من Java التي لا تدعم واجهة برمجة التطبيقات هذه. افترض أنك تريد استخدام فئة java.util.stream.LongStream ، المقدمة في Java 8 ، وتريد استخدام طريقة anyMatch(LongPredicate) من هذه الفئة ، ولكن يجب أن يكون التطبيق متوافقًا مع Java 7. يمكنك إنشاء فئة مساعدة ، مثل هذا:

 public classLongStreamHelper { private static Class longStreamClass; private static Class longPredicateClass; private static Method anyMatchMethod; static { try { longStreamClass = Class.forName("java.util.stream.LongStream"); longPredicateClass = Class.forName("java.util.function.LongPredicate"); anyMatchMethod = longStreamClass.getMethod("anyMatch", longPredicateClass): } catch (ClassNotFoundException e) { longStreamClass = null; longPredicateClass = null; anyMatchMethod = null } catch (NoSuchMethodException e) { longStreamClass = null; longPredicateClass = null; anyMatchMethod = null; } public static boolean anyMatch(Object theLongStream, Object thePredicate) throws NotImplementedException { if (longStreamClass == null) throw new NotImplementedException(); try { Boolean result = (Boolean) anyMatchMethod.invoke(theLongStream, thePredicate); return result.booleanValue(); } catch (Throwable e) { // lots of potential exceptions to handle. Let's simplify. throw new NotImplementedException(); } } } 

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

بدلاً من استدعاء theLongStream.anyMatch(thePredicate) ، كما تفعل في Java 8 ، يمكنك الاتصال LongStreamHelper.anyMatch(theLongStream, thePredicate) في أي إصدار من Java. إذا كنت تتعامل مع Java 8 ، فسوف يعمل هذا ، ولكن إذا كنت تتعامل مع Java 7 ، فسيقوم البرنامج بإلقاء NotImplementedException .

لماذا هذا قبيح؟ لأن الشفرة يمكن أن تصبح معقدة للغاية إذا كنت بحاجة إلى الوصول إلى العديد من واجهات برمجة التطبيقات (في الواقع ، حتى الآن ، مع واجهة برمجة تطبيقات واحدة ، فهذا غير مريح بالفعل). بالإضافة إلى ذلك ، هذه الممارسة ليست آمنة من حيث النوع ، حيث لا يمكن أن يشير الرمز مباشرة إلى LongStream أو LongPredicate . وأخيرًا ، فإن هذه الممارسة أقل كفاءة بكثير ، بسبب النفقات العامة للانعكاس ، بالإضافة إلى الكتل الإضافية try-catch . لذلك ، على الرغم من أنه يمكن القيام بذلك بهذه الطريقة ، إلا أنها ليست مثيرة للاهتمام للغاية ومليئة بالأخطاء بسبب الإهمال.

نعم ، يمكنك الوصول إلى واجهات برمجة التطبيقات الجديدة ، ويحافظ الرمز الخاص بك في نفس الوقت على التوافق مع الإصدارات السابقة ، ولكنك لن تنجح مع تركيبات اللغة الجديدة. على سبيل المثال ، لنفترض أننا بحاجة إلى استخدام تعبيرات lambda في التعليمات البرمجية التي يجب أن تظل متوافقة مع الإصدارات السابقة وتعمل في Java 7. أنت محظوظ. لن يسمح لك مترجم Java بتحديد نسخة من كود المصدر فوق الهدف. لذا ، إذا قمت بتعيين مستوى الامتثال لشفرة المصدر إلى 1.8 (أي ، Java 8) ، ومستوى الامتثال الهدف هو 1.7 (Java 7) ، فلن يسمح لك المترجم بذلك.

سوف تساعدك ملفات JAR متعددة الإصدارات

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

لا تختلف ملفات JAR متعددة الإصدارات تقريبًا عن ملفات JAR القديمة الجيدة ، ولكن مع تحذير واحد مهم: ظهر "مكانة" جديدة في ملفات JAR الجديدة ، حيث يمكنك كتابة الفصول التي تستخدم أحدث ميزات Java 9. إذا كنت تعمل مع Java 9 ، فعندئذٍ سيجد JVM هذا "المكان المناسب" ، وسيستخدم الفئات منه ويتجاهل الفئات التي تحمل الاسم نفسه من الجزء الرئيسي من ملف JAR.

ومع ذلك ، عند العمل مع Java 8 أو أقل ، فإن JVM لا تدرك وجود هذا "المكان المناسب". تتجاهلها وتستخدم فئات من الجزء الرئيسي من ملف JAR. مع إصدار Java 10 ، ستظهر "مكانة" جديدة مماثلة للفصول التي تستخدم الميزات الأكثر ملاءمة لـ Java 10 وما إلى ذلك.

في JEP 238 ، اقتراح لتطوير Java ، والذي يصف ملفات JAR الوعرة ، يتم تقديم مثال بسيط. لنفترض أن لدينا ملف JAR مع أربع فئات تعمل في Java 8 أو أقل:

 JAR root - A.class - B.class - C.class - D.class 

تخيل الآن أنه بعد إصدار Java 9 ، نعيد كتابة الفئتين A و B حتى يتمكنوا من استخدام الميزات الجديدة الخاصة بـ Java 9. ثم تخرج Java 10 ، ونعيد كتابة الفئة A بحيث يمكنها استخدام الميزات الجديدة لـ Java 10. ، يجب أن يعمل التطبيق بشكل جيد مع Java 8. يبدو ملف JAR الجديد متعدد الإصدارات كما يلي:

 JAR root - A.class - B.class - C.class - D.class - META-INF Versions - 9 - A.class - B.class - 10 - A.class 

لم يكتسب ملف JAR بنية جديدة فقط ؛ الآن في بيانه يشار إلى أن هذا الملف متعدد الإصدارات.

عند تشغيل ملف JAR هذا في Java 8 JVM ، فإنه يتجاهل قسم \META-INF\Versions لأنه لا يشك فيه أو يبحث عنه. يتم استخدام الفئات الأصلية A و B و C و D فقط.

عند التشغيل تحت Java 9 ، يتم استخدام الفئات الموجودة في \META-INF\Versions\9 ، علاوة على ذلك ، يتم استخدامها بدلاً من الفئات الأصلية A و B ، ولكن \META-INF\Versions\10 تجاهل الفئات في \META-INF\Versions\10 .

عند التشغيل تحت Java 10 ، يتم استخدام كلا الفرعين \META-INF\Versions ؛ على وجه الخصوص ، الإصدار A من Java 10 ، الإصدار B من Java 9 ، والإصدارات الافتراضية C و D.

لذا ، إذا كنت في تطبيقك تحتاج إلى واجهة برمجة تطبيقات ProcessBuilder الجديدة من Java 9 ، ولكنك تحتاج إلى التأكد من أن التطبيق يستمر في العمل ضمن Java 8 ، فما عليك سوى كتابة الإصدارات الجديدة من الفصول الدراسية باستخدام ProcessBuilder في قسم \META-INF\Versions\9 من ملف JAR ، وترك الفصول القديمة في الجزء الرئيسي من الأرشيف ، المستخدم افتراضيًا. هذه هي أسهل طريقة لاستخدام الميزات الجديدة لـ Java 9 دون التضحية بالتوافق مع الإصدارات السابقة.

يحتوي Java 9 JDK على إصدار من أداة jar.exe التي تدعم إنشاء ملفات JAR متعددة الإصدارات. توفر أدوات أخرى غير JDK هذا الدعم أيضًا.

جافا 9: وحدات ، وحدات في كل مكان

يعد نظام Java 9 Module (المعروف أيضًا باسم Project Jigsaw) بلا شك أكبر تغيير في Java 9. أحد أهداف تطبيق نظام الوحدات هو تعزيز آلية تغليف Java بحيث يمكن للمطور تحديد واجهات برمجة التطبيقات التي يتم توفيرها لمكونات أخرى ويمكن الاعتماد عليها ، أن JVM سيفرض التغليف. يعتبر التغليف أكثر قوة مع تطبيق نظام الوحدات النمطية مقارنة بمعدلات الوصول public/protected/private للفصول أو أعضاء الفصل.

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

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

أولاً ، يصبح ملف JAR معياريًا (ويتحول إلى وحدة نمطية) مع ظهور ملف module-info.class (الذي تم تجميعه من module-info.java) في جذر ملف JAR. module-info.java يحتوي على بيانات وصفية ، على وجه الخصوص ، اسم الوحدة التي يتم تصدير حزمها (أي تصبح مرئية من الخارج) ، والتي تتطلبها هذه الوحدة ، وبعض المعلومات الأخرى.

المعلومات الموجودة في module-info.class تكون مرئية فقط عندما يبحث عنها JVM - أي أن النظام يعالج ملفات JAR المعيارية تمامًا مثل العادي إذا كان يعمل مع الإصدارات القديمة من Java (من المفترض أن الرمز تم تجميعه للعمل مع إصدار أقدم من Java بالمعنى الدقيق للكلمة ، يتطلب الأمر القليل من الكيمياء ، ولا يزال Java 9 محددًا على أنه الإصدار المستهدف من module-info.class ، لكن هذا حقيقي).

وبالتالي ، يجب أن تكون قادرًا على تشغيل ملفات JAR المعيارية باستخدام Java 8 وما دونه ، شريطة أن تكون متوافقة أيضًا مع الإصدارات السابقة من Java في جوانب أخرى. لاحظ أيضًا أنه يمكن وضع module-info.class ، مع التحفظات ، في مناطق ذات إصدارات من ملفات JAR متعددة الإصدارات .

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

لاحظ أيضًا أن مجموعة جميع ملفات JAR في مسار الفصل تعتبر جزءًا من وحدة واحدة غير مسماة. تعتبر هذه الوحدة الأكثر شيوعًا ، ومع ذلك ، فهي تصدر جميع المعلومات إلى وحدات أخرى ، ويمكن أن تشير إلى أي وحدات أخرى. وبالتالي ، إذا لم يكن لديك حتى الآن تطبيق Java معياري ، ولكن هناك بعض المكتبات القديمة التي لم يتم أيضًا تعديلها (وربما لن يحدث ذلك أبدًا) - يمكنك ببساطة وضع كل هذه المكتبات في مسار الفصل الدراسي ، وسيعمل النظام بأكمله بشكل جيد.

تحتوي Java 9 على مسار وحدة نمطية يعمل مع مسار الفصل الدراسي. عند استخدام وحدات من هذا المسار ، يمكن لـ JVM التحقق (سواء في وقت الترجمة أو في وقت التشغيل) مما إذا كانت جميع الوحدات الضرورية في مكانها والإبلاغ عن خطأ في حالة عدم وجود أي وحدات. جميع ملفات JAR في مسار الفصل ، كأعضاء في وحدة غير مسماة ، يمكن الوصول إليها من قبل الوحدات المدرجة في المسار المعياري - والعكس صحيح.

ليس من الصعب نقل ملف JAR من مسار الفصل إلى مسار الوحدة النمطية - والاستفادة الكاملة من تطبيق نظام الوحدات. أولاً ، يمكنك إضافة ملف module-info.class إلى ملف JAR ، ثم وضع ملف JAR المعياري في مسار الوحدات. ستظل هذه الوحدة التي تم إنشاؤها حديثًا قادرة على الوصول إلى جميع ملفات JAR المتبقية في مسار JAR ، لأنها تدخل الوحدة غير المسماة وتبقى في الوصول.

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

تعتبر الوحدة التلقائية وحدة نمطية ، حتى لو لم يكن لديها ملف module-info.class . هذه الوحدة هي نفس اسم ملف JAR الذي يحتويه ، ويمكن للوحدات الأخرى طلبها بشكل صريح. يقوم تلقائيًا بتصدير جميع واجهات برمجة التطبيقات المتاحة للجمهور ويقرأ (أي يتطلب) جميع الوحدات الأخرى المسماة وكذلك الوحدات التي لا تحمل اسمًا.

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

لا يمكن نقل جميع ملفات JAR غير المعيارية إلى مسار الوحدة النمطية وتحويلها إلى وحدة نمطية تلقائية. هناك قاعدة: يمكن أن تكون الحزمة جزءًا من وحدة واحدة مسماة فقط . بمعنى ، إذا كانت الحزمة موجودة في أكثر من ملف JAR ، فيمكن تحويل ملف JAR واحد فقط مع هذه الحزمة في التكوين إلى وحدة نمطية تلقائية. يمكن أن تبقى الباقية في مسار الفصل والانضمام إلى الوحدة النمطية غير المسماة.

للوهلة الأولى ، تبدو الآلية الموصوفة هنا معقدة ، ولكنها في الواقع عملية بسيطة للغاية. في الواقع ، في هذه الحالة ، يمكنك فقط ترك ملفات JAR القديمة في مسار الفصل الدراسي أو نقلها إلى مسار الوحدة النمطية. يمكنك تقسيمها إلى وحدات أم لا. وعندما يتم تعديل ملفات JAR القديمة ، يمكنك تركها في مسار الفصل الدراسي أو نقلها إلى مسار الوحدة النمطية.

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

Java 9 "افعلها بنفسك": وحدات JDK و Jlink

إحدى مشكلات تطبيقات Java القديمة هي أن المستخدم النهائي قد لا يعمل مع بيئة Java مناسبة. تتمثل إحدى طرق ضمان صحة تطبيق Java في توفير وقت تشغيل مع التطبيق. تسمح لك Java بإنشاء JREs (قابلة لإعادة التوزيع) خاصة يمكن توزيعها داخل التطبيق. إليك كيفية إنشاء JRE خاص. كقاعدة ، يتم أخذ التسلسل الهرمي لملف JRE ، والذي يتم تثبيته مع JDK ، ويتم حفظ الملفات الضرورية ، ويتم حفظ الملفات الاختيارية مع الوظائف التي قد تكون مطلوبة في تطبيقك.

العملية مزعجة بعض الشيء: يجب عليك الحفاظ على التسلسل الهرمي لملفات التثبيت ، مع توخي الحذر ، حتى لا تفوت ملفًا واحدًا ، وليس دليلًا واحدًا. هذا في حد ذاته لن يضر ، ومع ذلك ، ما زلت تريد التخلص من كل ما هو غير ضروري ، لأن هذه الملفات تشغل مساحة. نعم ، من السهل الاستسلام وارتكاب مثل هذا الخطأ.

فلماذا لا تفوض هذا العمل إلى JDK؟

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

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

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

لاحظ أيضًا: إذا قمت بتشغيل jlink مع تطبيق لا يتم فيه تعديل أي شيء ، فإن الأداة ببساطة ليس لديها المعلومات الضرورية لضغط JRE ، لذلك لن يتبقى jlink سوى حزم JRE بالكامل. حتى في هذه الحالة ، سيكون الأمر أكثر ملاءمة لك: سيقوم jlink بحزم JRE لك ، لذلك لا داعي للقلق بشأن كيفية نسخ التسلسل الهرمي للملف بشكل صحيح.

مع jlink ، يصبح من السهل حزم التطبيق وكل ما تحتاجه لتشغيله - ولا داعي للقلق من أنك ستفعل شيئًا خاطئًا. ستقوم الأداة فقط بتعبئة جزء من وقت التشغيل المطلوب لتشغيل التطبيق. وهذا يعني أن تطبيق Java القديم مضمون لاستقبال بيئة سيتم تشغيلها فيها.

لقاء القديم والجديد

, Java- , , . Java 9, , API , ( ) , , Java.

Java 9: -, , , Java.

JAR- Java 9 JAR-, Java . , Java 9, Java 8 – .

Java, , JAR- , . , , « » .

JDK jlink, , . , Java – .

Java, Java 9 , – , , Java.

Source: https://habr.com/ru/post/ar417733/


All Articles