الجزء الأول هل أحتاج لقراءة هذا المقال؟
يعد محمل فئة Java أمرًا بالغ الأهمية لتشغيل نظام Java، ولكن غالبًا ما نتجاهله. يقوم محمل فئة Java بتحميل الفئات في وقت التشغيل من خلال البحث عنها وتحميلها. يمكن لبرامج تحميل الفئات المخصصة تغيير طريقة تحميل الفئات بشكل كامل، وتخصيص جهاز Java الظاهري الخاص بك بالطريقة التي تريدها. تقدم هذه المقالة بإيجاز مُحمل فئة Java، ثم توضح ذلك من خلال مثال لإنشاء مُحمل فئة مخصص، وسيقوم مُحمل الفئة هذا تلقائيًا بتجميع التعليمات البرمجية قبل تحميل الفئة. ستتعرف على ما يفعله محمل الفصل بالفعل وكيفية إنشاء محمل خاص بك. طالما أن لديك بعض المعرفة الأساسية بـ Java، وتعرف كيفية إنشاء برنامج Java لسطر الأوامر وتجميعه وتشغيله وبعض المفاهيم الأساسية لملفات فئة Java، فيمكنك فهم محتوى هذه المقالة. بعد قراءة هذه المقالة يجب أن تكون قادرًا على:
* توسيع وظائف جهاز Java الظاهري
* إنشاء محمل فئة مخصصة
* كيفية دمج محمل فئة مخصصة في التطبيق الخاص بك
* قم بتعديل محمل الفصل الخاص بك ليكون متوافقًا مع Java 2
الجزء 2. مقدمة ما هو مُحمل الفئة؟
الفرق بين Java واللغات الأخرى هو أن Java تعمل على Java Virtual Machine (JVM). وهذا يعني أنه يتم حفظ التعليمات البرمجية المترجمة بتنسيق مستقل عن النظام الأساسي، بدلاً من التنسيق الذي يتم تشغيله على جهاز معين. يحتوي هذا التنسيق على العديد من الاختلافات المهمة عن تنسيق التعليمات البرمجية القابل للتنفيذ التقليدي. على وجه التحديد، على عكس برنامج C أو C++، فإن برنامج Java ليس ملفًا مستقلاً قابلاً للتنفيذ، ولكنه يتكون من العديد من ملفات الفئات المنفصلة، كل ملف فئة يتوافق مع فئة Java. بالإضافة إلى ذلك، لا يتم تحميل ملفات الفئات هذه إلى الذاكرة على الفور، ولكن يتم تحميلها عندما يحتاج البرنامج إليها. أداة تحميل الفئة هي أداة تستخدم في جهاز Java الظاهري لتحميل الفئات في الذاكرة. علاوة على ذلك، يتم تطبيق محمل فئة Java أيضًا في Java. بهذه الطريقة يمكنك بسهولة إنشاء أداة تحميل الفئة الخاصة بك دون الحاجة إلى فهم متعمق لجهاز Java الظاهري.
لماذا إنشاء محمل فئة؟
الآن بعد أن أصبح لدى Java Virtual Gold مُحمل فئة بالفعل، هل نحتاج إلى إنشاء برامج أخرى بأنفسنا؟ يعرف مُحمل الفئة الافتراضي فقط كيفية تحميل الفئات من النظام المحلي. عندما يتم تجميع برنامجك بالكامل محليًا، فإن مُحمل الفئة الافتراضي يعمل بشكل جيد بشكل عام. لكن أحد أكثر الأشياء إثارة في Java هو مدى سهولة تحميل الفئات من الشبكة بدلاً من تحميلها محليًا فقط.
على سبيل المثال، يمكن للمتصفح تحميل الفئات من خلال أداة تحميل فئة مخصصة. هناك أيضًا العديد من الطرق لتحميل الفصول الدراسية. أحد الأشياء الأكثر إثارة في Java هو أنه يمكنك تخصيصها بالإضافة إلى ذلك ببساطة من الشبكة المحلية أو الشبكة:
* التحقق تلقائيًا من التوقيعات الرقمية قبل تنفيذ تعليمات برمجية غير موثوقة
* فك تشفير الكود بناءً على كلمة المرور المقدمة من المستخدم
* إنشاء فئات ديناميكيًا وفقًا لاحتياجات المستخدم يمكن دمجه بسهولة في التطبيق الخاص بك في شكل رمز ثانوي. أمثلة على أدوات تحميل الفئات المخصصة إذا كنت قد استخدمت برنامج JDK (Java Software Development Kit) (متصفح التطبيقات الصغيرة) أو غيره.
بالنسبة لمتصفحات Java المضمنة، فإنك تستخدم بالفعل أداة تحميل فئة مخصصة. عندما أصدرت شركة Sun لغة Java لأول مرة، كان أحد أكثر الأشياء إثارة هو مشاهدة كيفية تنفيذ Java للتعليمات البرمجية التي تم تنزيلها من موقع ويب بعيد. التنفيذ من موقع بعيد عبر HTTP
يبدو رمز البايت الذي يرسله اتصال P غريبًا بعض الشيء. يعمل هذا لأن Java لديها القدرة على تثبيت أدوات تحميل فئة مخصصة. يحتوي متصفح التطبيق الصغير على مُحمل فئة. لا يعثر مُحمل الفئة هذا على فئات Java محليًا، وبدلاً من ذلك، فإنه يصل إلى الخادم البعيد، ويقوم بتحميل ملف الرمز الثانوي الأصلي من خلال HTTP، ثم يقوم بتحويله إلى فئة Java في جهاز Java الظاهري. بالطبع تقوم أدوات تحميل الفئات بالكثير من الأشياء الأخرى: فهي تحظر فئات Java غير الآمنة وتمنع التطبيقات الصغيرة الموجودة على صفحات مختلفة من التدخل مع بعضها البعض. Echidna، وهي حزمة كتبها Luke Gorrie، هي حزمة برامج Java مفتوحة تسمح بتشغيل تطبيقات Java المتعددة بأمان في جهاز Java الظاهري. يمنع التداخل بين التطبيقات باستخدام مُحمل فئة مخصص لإعطاء كل تطبيق نسخة من ملف الفئة.
مثال مُحمل الفئة الخاص بنا الآن بعد أن عرفنا كيفية عمل مُحمل الفئة وكيفية تحديد مُحمل الفئة الخاص بنا، نقوم بإنشاء مُحمل فئة مخصص يسمى CompilingClassLoader (CCL). تقوم CCL بعملية التجميع نيابةً عنا، لذلك لا يتعين علينا تجميعها يدويًا بأنفسنا. وهذا يعادل في الأساس وجود برنامج "إنشاء" يتم دمجه في بيئة التشغيل الخاصة بنا.
ملاحظة: قبل أن ننتقل إلى الخطوة التالية، من الضروري فهم بعض المفاهيم ذات الصلة.
لقد تم تحسين النظام بشكل كبير في الإصدار 1.2 من JDK (وهو ما نسميه منصة Java 2). تمت كتابة هذه المقالة تحت JDK 1.0 و1.1، ولكن كل شيء سيعمل في الإصدارات الأحدث. تم أيضًا تحسين ClassLoader في Java2.
وترد مقدمة مفصلة في الجزء الخامس.
الجزء 3. نظرة عامة على بنية ClassLoader الغرض الأساسي لمحمل الفئة هو خدمة طلبات فئات Java. عندما يحتاج جهاز Java الظاهري إلى فئة، فإنه يعطي اسم فئة لمحمل الفئة، ثم يحاول محمل الفئة إرجاع مثيل الفئة المقابلة. يمكن إنشاء أدوات تحميل فئة مخصصة عن طريق تجاوز الطرق المقابلة في مراحل مختلفة. بعد ذلك سوف نتعرف على بعض الطرق الرئيسية لمحمل الفصل. ستفهم ما تفعله هذه الطرق وكيفية عملها عند تحميل ملفات الفصل الدراسي. ستعرف أيضًا الرمز الذي تحتاج إلى كتابته عند إنشاء أداة تحميل فئة مخصصة. في الجزء التالي، ستستفيد من هذه المعرفة وCompilingCl المخصص لدينا
يعمل assLoader معًا.
طريقة تحميل الفئة
ClassLoader.loadClass() هي نقطة الدخول إلى ClassLoader. توقيع الطريقة هو كما يلي:
فئة LoadClass(اسم السلسلة، الحل المنطقي)؛
يحدد اسم المعلمة الاسم الكامل للفئة (بما في ذلك اسم الحزمة) المطلوبة بواسطة جهاز Java الظاهري، مثل Foo أو java.lang.Object.
تحدد معلمة الحل ما إذا كان الفصل يحتاج إلى حل أم لا. يمكنك فهم دقة الفصل على أنها جاهزة تمامًا للتشغيل. التحليل غير مطلوب بشكل عام. إذا كان جهاز Java الظاهري يريد فقط معرفة ما إذا كانت هذه الفئة موجودة أو يريد معرفة فئتها الأصلية، فإن التحليل غير ضروري على الإطلاق. في Java 1.1 والإصدارات السابقة، إذا كنت تريد تخصيص مُحمل الفئة، فإن طريقة LoadClass هي الطريقة الوحيدة التي تحتاج إلى تجاوزها في الفئة الفرعية.
(تم تغيير ClassLoader في Java1.2 وقدم الطريقة findClass()).
this.methoddefineClass
تعتبر DefineClass طريقة غامضة جدًا في ClassLoader. تقوم هذه الطريقة بإنشاء مثيل فئة من صفيف بايت. قد تأتي مجموعة البايت الأولية هذه التي تحتوي على بيانات من نظام الملفات أو من الشبكة. يوضح DefineClass التعقيد والغموض والاعتماد على النظام الأساسي لجهاز Java Virtual Machine - فهو يفسر الكود الثانوي لتحويله إلى هياكل بيانات وقت التشغيل، والتحقق من صحتها، والمزيد. لكن لا تقلق، ليس عليك القيام بأي من هذا. في الواقع، لا يمكنك تجاوز ذلك على الإطلاق،
لأنه يتم تعديل الطريقة بواسطة الكلمة الأساسية Final.
MethodfindSystemClass
تقوم طريقة findSystemClass بتحميل الملفات من النظام المحلي. يبحث عن ملفات الفئة على النظام المحلي، وإذا وجدت، المكالمات
يقوم DefineClass بتحويل صفيف البايت الأصلي إلى كائن فئة. هذه هي الآلية الافتراضية لجهاز Java الظاهري لتحميل الفئات عند تشغيل تطبيقات Java. بالنسبة إلى أدوات تحميل الفئات المخصصة، نحتاج فقط إلى استخدام findSystemClass بعد فشلنا في التحميل. السبب بسيط: مُحمِّل الفصل لدينا مسؤول عن تنفيذ خطوات معينة في تحميل الفصل، ولكن ليس كل الفئات. على سبيل المثال،
حتى لو قام محمل الفصل الخاص بنا بتحميل بعض الفئات من الموقع البعيد، فلا يزال هناك العديد من الفئات الأساسية التي تحتاج إلى التحميل من النظام المحلي.
هذه الفئات لا تهمنا، لذلك نسمح لجهاز Java الظاهري بتحميلها بالطريقة الافتراضية: من النظام المحلي. هذا ما يفعله findSystemClass. العملية برمتها تقريبًا كما يلي:
* يطلب جهاز Java الظاهري من محمل الفصل المخصص لدينا تحميل الفصل.
* نتحقق مما إذا كان الموقع البعيد يحتوي على الفئة التي يجب تحميلها.
* إذا كان هناك، نحصل على هذا الفصل.
* إذا لم يكن الأمر كذلك، فإننا نعتقد أن هذا الفصل موجود في مكتبة الفصل الأساسية ونستدعي findSystemClass لتحميله من نظام الملفات.
في معظم أدوات تحميل الفئات المخصصة، يجب عليك الاتصال بـ findSystemClass أولاً لتوفير وقت البحث من جهاز التحكم عن بُعد.
في الواقع، كما سنرى في القسم التالي، يُسمح لجهاز Java الظاهري بتحميل الفئات من نظام الملفات المحلي فقط عندما نتأكد من أننا قمنا بتجميع التعليمات البرمجية الخاصة بنا تلقائيًا.
طريقة حل الفئة
كما ذكر أعلاه، يمكن تقسيم سجلات الفصل إلى تحميل جزئي (بدون تحليل) وتحميل كامل (بما في ذلك التحليل). عندما نقوم بإنشاء محمل فئة مخصصة، قد نحتاج إلى استدعاء ResolveClass.
MethodfindLoadedClass
ينفذ findLoadedClass ذاكرة تخزين مؤقت: عندما يكون LoadClass مطلوبًا لتحميل فئة، يمكنك أولاً استدعاء هذه الطريقة لمعرفة ما إذا كان قد تم تحميل الفئة لمنع إعادة تحميل فئة محملة بالفعل. يجب استدعاء هذه الطريقة أولاً، دعونا نلقي نظرة على كيفية تنظيم هذه الطرق معًا.
يقوم مثالنا على تنفيذ LoadClass بتنفيذ الخطوات التالية. (نحن لا نحدد تقنية محددة للحصول على ملف الفئة - قد يكون من الشبكة، من حزمة مضغوطة أو مجمعة ديناميكيًا. على أي حال، ما نحصل عليه هو ملف البايت كود الأصلي)
* اتصل بـ findLoadedClass للتحقق مما إذا كان قد تم تحميل هذا الفصل أم لا.
* إذا لم يتم تحميله، فسنحصل على مصفوفة البايت الأصلية بطريقة أو بأخرى.
* إذا تم الحصول على المصفوفة، فاتصل بـdefineClass لتحويلها إلى كائن فئة.
* إذا تعذر الحصول على مصفوفة البايت الأصلية، فاتصل بـ findSystemClass للتحقق مما إذا كان من الممكن تسجيلها من نظام الملفات المحلي.
* إذا كان حل المعلمة صحيحًا، فاستدعاء ResolveClass لحل كائن الفئة.
* إذا لم يتم العثور على الفئة، فقم بطرح ClassNotFoundException.
* وإلا قم بإرجاع هذا الفصل.
الآن بعد أن أصبح لدينا فهم أكثر شمولاً للمعرفة العملية لمُحمل الفئة، يمكننا إنشاء مُحمل فئة مخصص. في القسم التالي، سنناقش CCL.
الجزء 4. تجميع ClassLoader
يوضح لنا CCL وظيفة مُحمل الفئة. الغرض من CCL هو تمكين تجميع التعليمات البرمجية الخاصة بنا وتحديثها تلقائيًا. وإليك كيف يعمل:
* عندما يكون هناك طلب لفئة ما، تحقق أولاً مما إذا كان ملف الفئة موجودًا في الدليل الحالي والدلائل الفرعية للقرص.
* إذا لم يكن هناك ملف فئة، ولكن يوجد ملف تعليمات برمجية مصدر، فاتصل بمترجم Java لتجميع ملف الفئة وإنشائه.
* إذا كان ملف الفئة موجودًا بالفعل، فتحقق مما إذا كان ملف الفئة أقدم من ملف التعليمات البرمجية المصدر. إذا كان ملف الفئة أقدم من ملف التعليمات البرمجية المصدر، فاتصل بمترجم Java لإعادة إنشاء ملف الفئة.
* إذا فشل التجميع، أو لا يمكن إنشاء ملف الفئة من الملف المصدر لأسباب أخرى، فقم بطرح الاستثناء ClassNotFou
ndException.
* إذا لم تكن قد حصلت على هذا الفصل بعد، فقد يكون موجودًا في مكتبات فئات أخرى. اتصل بـ findSystemClass لمعرفة ما إذا كان من الممكن العثور عليه.
* إذا لم يتم العثور عليه، قم بطرح ClassNotFoundException.
* وإلا قم بإرجاع هذا الفصل.
كيف يتم تنفيذ تجميع جافا؟
قبل أن نذهب إلى أبعد من ذلك، نحن بحاجة إلى فهم عملية تجميع جافا. عادةً، يقوم مترجم Java بتجميع تلك الفئات المحددة فقط. سيقوم أيضًا بتجميع الفئات الأخرى ذات الصلة إذا كانت الفئات المحددة مطلوبة. سيقوم CCL بتجميع الفئات التي نحتاج إلى تجميعها في التطبيق واحدًا تلو الآخر. ومع ذلك، بشكل عام، بعد أن يقوم المترجم بتجميع الفئة الأولى،
سوف تجد CCL أن الفئات الأخرى ذات الصلة المطلوبة قد تم تجميعها بالفعل. لماذا؟ يستخدم مترجم Java قواعد مشابهة كما فعلنا: إذا لم يكن الفصل موجودًا أو تم تحديث الملف المصدر، فسيتم تجميع الفصل. يتقدم مترجم Java بشكل أساسي على CCL بخطوة واحدة، ويتم تنفيذ معظم العمل بواسطة مترجم Java. يبدو أن CCL تقوم بتجميع هذه الفئات.
في معظم الحالات، ستجد أنه يتم استدعاء المترجم في فئة الوظيفة الرئيسية، وهذا كل شيء - مكالمة بسيطة كافية. ومع ذلك، هناك حالة خاصة حيث لا يتم تجميع هذه الفئات في المرة الأولى التي تظهر فيها. إذا قمت بتحميل فئة بناءً على اسمها، باستخدام الطريقة Class.forName، فلن يعرف مترجم Java ما إذا كانت الفئة مطلوبة أم لا. في هذه الحالة،
تجد أن CCL يستدعي المترجم مرة أخرى لتجميع الفصل. يوضح الكود الموجود في القسم 6 هذه العملية.
باستخدام CompilationClassLoader
من أجل استخدام CCL، لا يمكننا تشغيل برنامجنا مباشرة، يجب تشغيله بطريقة خاصة، مثل هذا:
% جافا فو arg1 arg2
نقوم بتشغيله مثل هذا:
% جافا CCLRun Foo arg1 arg2
CCLRun هو برنامج خاص يقوم بإنشاء CompilingClassLoader ويستخدمه لتحميل فئة الوظيفة الرئيسية لدينا، مما يضمن تحميل البرنامج بالكامل بواسطة CompilingClassLoader. يستخدم CCLrun Ja
تستدعي واجهة برمجة تطبيقات va Reflect الوظيفة الرئيسية لفئة الوظيفة الرئيسية وتمرر المعلمات إلى هذه الوظيفة. لمعرفة المزيد، راجع كود المصدر في الجزء السادس.
لنقم بتشغيل المثال لتوضيح كيفية عمل العملية برمتها.
البرنامج الرئيسي هو فئة تسمى Foo، والتي تقوم بإنشاء مثيل للفئة Bar. يقوم مثيل Bar بدوره بإنشاء مثيل للفئة Baz، الموجودة في الحزمة baz. وهذا لتوضيح كيفية تحميل CCL للفئات من الحزم الفرعية. يقوم الشريط أيضًا بتحميل الفئة Boo بناءً على اسم الفئة
، ويتم ذلك أيضًا بواسطة CCL. تم تحميل كافة الفئات وجاهزة للتشغيل. استخدم الكود المصدري من الفصل السادس لتنفيذ هذا البرنامج. ترجمة CCLRun وCompilingClassLoader. تأكد من عدم تجميع فئات أخرى (Foo، Bar، Baz، a
nd Boo)، وإلا فلن يعمل CCL.
% جافا CCLRun Foo arg1 arg2
CCL: تجميع Foo.java...
فو!
شريط!
باز!
CCL: تجميع Boo.java...
بوو!
لاحظ أنه تم استدعاء المترجم لأول مرة لـ Foo.java، كما يتم أيضًا تجميع Bar وbaz.Baz معًا. ومثل بو
عندما تحتاج القناة إلى التحميل، يستدعي CCL المترجم مرة أخرى لتجميعها.
الجزء 5. نظرة عامة على تحسينات أداة تحميل الفئة في Java 2 في Java 1.2 والإصدارات الأحدث، تم تحسين أداة تحميل الفئة بشكل كبير. لا يزال الكود القديم يعمل، لكن النظام الجديد يجعل تنفيذنا أسهل. هذا النموذج الجديد هو نموذج تفويض الوكيل، مما يعني أنه إذا لم يتمكن مُحمل الفئة من العثور على فئة، فسوف يطلب من مُحمل الفئة الأصل العثور عليها. مُحمل فئة النظام هو سلف جميع أدوات تحميل الفئة. يقوم مُحمل فئة النظام بتحميل الفئات بشكل افتراضي، أي من نظام الملفات المحلي. عادةً ما يحاول تجاوز طريقة LoadClass تحميل الفصل بعدة طرق. إذا كتبت الكثير من أدوات تحميل الفصل، فستجد أنك تقوم فقط بإجراء بعض التعديلات على هذه الطريقة المعقدة مرارًا وتكرارًا. يتضمن التنفيذ الافتراضي لـloadClass في Java 1.2 الطريقة الأكثر شيوعًا للعثور على فئة، مما يسمح لك بتجاوز طريقة findClass وloadClass لاستدعاء طريقة findClass بشكل مناسب. وتتمثل ميزة ذلك في أنك لا تحتاج إلى تجاوز LoadClass، ما عليك سوى تجاوز findClass، مما يقلل من عبء العمل.
طريقة جديدة: findClass
يتم استدعاء هذه الطريقة من خلال التنفيذ الافتراضي لـloadClass. الهدف من findClass هو تضمين جميع التعليمات البرمجية الخاصة بمحمل الفصل،
ليست هناك حاجة لتكرار الكود (مثل استدعاء محمل فئة النظام عند فشل الطريقة المحددة).
طريقة جديدة: getSystemClassLoader
بغض النظر عما إذا كنت قد تجاوزت طريقتي findClass وloadClass أم لا، يمكن لأسلوب getSystemClassLoader الوصول مباشرة إلى محمل فئة النظام (بدلاً من الوصول غير المباشر من خلال findSystemClass).
طريقة جديدة: getParent
من أجل تفويض الطلب إلى محمل الفئة الأصل، يمكن الحصول على محمل الفئة الأصل لمحمل الفئة هذا من خلال هذه الطريقة. يمكنك تفويض الطلب إلى مُحمل الفئة الأصل عندما لا تتمكن طريقة معينة في مُحمل فئة مخصصة من العثور على الفئة. يحتوي مُحمل الفئة الأصل لمحمل الفئة على الكود الذي يُنشئ مُحمل الفئة.
الجزء 6. كود المصدر
CompilingClassLoader.java
ما يلي هو محتوى الملف CompilingClassLoader.java
استيراد java.io.*;
/*
يقوم CompilingClassLoader بتجميع ملفات مصدر Java بشكل ديناميكي. فهو يتحقق من وجود الملف .class وما إذا كان الملف .class أقدم من الملف المصدر.
*/
يمتد CompilingClassLoader من الفئة العامة إلى ClassLoader
{
// حدد اسم الملف، واقرأ محتوى الملف بالكامل من القرص، وأرجع مصفوفة بايت.
البايت الخاص[] getBytes(اسم ملف السلسلة) يلقي IOException {
// احصل على حجم الملف.
ملف الملف = ملف جديد (اسم الملف)؛
لين طويل = file. length();
// أنشئ مصفوفة تكفي لتخزين محتويات الملف.
بايت خام[] = بايت جديد[(int)len];
//فتح الملف
FileInputStream fin = new FileInputStream( file );
// اقرأ كل المحتوى. إذا لم تتمكن من قراءته، حدث خطأ.
int r = fin.read(raw);
إذا (ص! = لين)
طرح IOException جديد( "لا يمكن قراءة الكل،"+r+" != "+len );
// لا تنس إغلاق الملف.
fin. Close();
// إرجاع هذه المصفوفة.
العودة الخام.
}
// قم بإنشاء عملية لتجميع ملف Java المصدر المحدد وتحديد معلمات الملف إذا كان التجميع ناجحًا، فارجع صحيحًا، وإلا،
// إرجاع خطأ.
ترجمة منطقية خاصة (سلسلة javaFile) تطرح IOException {
//إظهار التقدم الحالي
System.out.println( "CCL: تجميع "+javaFile+"...");
// ابدأ المترجم
العملية p = Runtime.getRuntime().exec( "javac "+javaFile );
// انتظر حتى ينتهي التجميع
يحاول {
انتظر () ؛
} Catch( InterruptedException ie ) { System.out.println( ie });
// تحقق من رمز الإرجاع لمعرفة ما إذا كانت هناك أخطاء في الترجمة.
int ret = p.exitValue();
// إرجاع ما إذا كان التجميع ناجحًا.
إرجاع ret==0;
}
// الكود الأساسي لمحمل الفصل - تقوم فئات التحميل تلقائيًا بتجميع الملفات المصدر عند الحاجة.
فئة التحميل العامة (اسم السلسلة، الحل المنطقي)
يرمي ClassNotFoundException {
// هدفنا هو الحصول على كائن فئة.
فئة الفصل = فارغة؛
// أولاً، تحقق مما إذا كانت هذه الفئة قد تمت معالجتها.
clas = findLoadedClass( name );
//System.out.println( "findLoadedClass: "+clas );
// احصل على اسم المسار من خلال اسم الفئة، على سبيل المثال: java.lang.Object => java/lang/Object
String fileStub = name.replace( ''''.'''', ''''/'''' );
// إنشاء كائنات تشير إلى الملفات المصدر وملفات الفئة.
String javaFilename = fileStub+".java";
String classFilename = fileStub+".class";
File javaFile = new File( javaFilename );
File classFile = new File( classFilename );
//System.out.println( "j "+javaFile.lastModified()+" c "
//+classFile.lastModified() );
// أولاً، حدد ما إذا كان التجميع مطلوبًا أم لا. إذا كان الملف المصدر موجودًا ولكن ملف الفئة غير موجود، أو كلاهما موجود ولكن الملف المصدر
// أحدث، مما يشير إلى أنه يحتاج إلى تجميع.
إذا (javaFile.exists() &&(!classFile.exists() ||
javaFile.lastModified() > classFile.lastModified())) {
يحاول {
// ترجمة، إذا فشل التجميع، يجب أن نعلن سبب الفشل (مجرد استخدام الفئات القديمة لا يكفي).
إذا (! ترجمة ( javaFilename ) || !classFile.exists()) {
طرح ClassNotFoundException الجديد("فشل الترجمة:"+javaFilename );
}
} قبض على (IOException أي) {
// قد يحدث خطأ في الإدخال/الإخراج أثناء التجميع.
رمي ClassNotFoundException(ie.toString());
}
}
// تأكد من أنه قد تم تجميعه بشكل صحيح أو أنه لا يتطلب التجميع، نبدأ في تحميل البايتات الأولية.
يحاول {
// قراءة البايتات.
بايت الخام[] = getBytes( classFilename );
// تحويل إلى كائن فئة
clas = DefineClass( name, الخام, 0, الخام. الطول );
} قبض على (IOException أي) {
// هذا لا يعني الفشل، ربما الفئة التي نتعامل معها موجودة في مكتبة الفئة المحلية، مثل java.lang.Object.
}
//System.out.println( "defineClass: "+clas );
// ربما يتم تحميلها في مكتبة الفصل بالطريقة الافتراضية.
إذا (الفئة == فارغة) {
clas = findSystemClass( name );
}
//System.out.println( "findSystemClass: "+clas );
// إذا كان حل المعلمة صحيحًا، فقم بتفسير الفئة حسب الحاجة.
إذا (حل && clas != null)
ResolveClass(clas);
// إذا لم يتم الحصول على الفصل، فقد حدث خطأ ما.
إذا (الفئة == فارغة)
طرح ClassNotFoundException(اسم) جديد؛
// بخلاف ذلك، قم بإرجاع كائن الفئة هذا.
عودة الطبقة؛
}
}
CCrun.java
هنا هو ملف CCRun.java
import java.lang.reflect.*;
/*
يقوم CCLRun بتحميل الفئات من خلال CompilingClassLoader لتشغيل البرنامج.
*/
الفئة العامة CCLRun
{
ثابت الفراغ العام الرئيسي (سلسلة الحجج[]) يلقي استثناء {
// تحدد المعلمة الأولى فئة الوظيفة الرئيسية التي يريد المستخدم تشغيلها.
String progClass = args[0];
// المعلمات التالية هي المعلمات التي تم تمريرها إلى فئة الوظيفة الرئيسية هذه.
String progArgs[] = new String[args.length-1];
System.arraycopy( args, 1, progArgs, 0, progArgs.length );
// إنشاء CompilingClassLoader
CompilingClassLoader ccl = new CompilingClassLoader();
// قم بتحميل فئة الوظيفة الرئيسية من خلال CCL.
فئة clas = ccl.loadClass(progClass );
// استخدم الانعكاس لاستدعاء وظيفته الرئيسية وتمرير المعلمات.
// قم بإنشاء كائن فئة يمثل نوع المعلمة للوظيفة الرئيسية.
Class mainArgType[] = { (new String[0]).getClass() };
// ابحث عن الوظيفة الرئيسية القياسية في الفصل.
الطريقة الرئيسية = clas.getMethod( "main", mainArgType );
// أنشئ قائمة معلمات - في هذه الحالة، مجموعة من السلاسل.
Object argsArray[] = { progArgs };
// اتصل بالوظيفة الرئيسية.
main.invoc( null, argsArray );
}
}
فو.جافا
فيما يلي محتوى الملف Foo.java
الطبقة العامة فو
{
ثابت الفراغ العام الرئيسي (سلسلة الحجج[]) يلقي استثناء {
System.out.println( "foo! "+args[0]+" "+args[1] );
شريط جديد( args[0], args[1] );
}
}
جافا
وفيما يلي محتوى الملف Bar.java
استيراد باز.*;
شريط الطبقة العامة
{
شريط عام (سلسلة أ، سلسلة ب) {
System.out.println( "bar! "+a+" "+b );
جديد باز (أ، ب)؛
يحاول {
Class booClass = Class.forName( "Boo" );
Object boo = booClass.newInstance();
} قبض (استثناء ه) {
printStackTrace();
}
}
}
baz/Baz.java
فيما يلي محتوى الملف baz/Baz.java
حزمة باز؛
الطبقة العامة الباز
{
باز العامة (سلسلة أ، سلسلة ب) {
System.out.println( "baz! "+a+" "+b );
}
}
Boo.java
فيما يلي محتوى الملف Boo.java
الطبقة العامة بو
{
عامة بو () {
System.out.println("بوو!");
}
}
الجزء 7. ملخص الملخص بعد قراءة هذه المقالة، هل أدركت أن إنشاء مُحمل فئة مخصص يسمح لك بالتعمق في الأجزاء الداخلية لجهاز Java الظاهري. يمكنك تحميل ملف فئة من أي مورد، أو إنشائه ديناميكيًا، بحيث يمكنك القيام بالكثير من الأشياء التي تهمك من خلال توسيع هذه الوظائف، ويمكنك أيضًا إكمال بعض الوظائف القوية.
موضوعات أخرى حول ClassLoader كما ذكرنا في بداية هذه المقالة، تلعب أدوات تحميل الفئات المخصصة دورًا مهمًا في متصفحات Java المضمنة ومتصفحات التطبيقات الصغيرة.