الخلط
في الماضي ، عند النظر إلى الكود المصدري ، واجهت دائمًا الرمز في الإطار باستخدام Thread.currentThread.getContextClassLoader () للحصول على محمل فئة السياق من مؤشر الترابط الحالي ، واستخدم محمل فئة السياق هذا لتحميل الفئة.
عندما نكتب عادةً رمزًا في البرنامج ، عندما نريد تحميل الفصول ديناميكية ، فإننا عادة ما نستخدم class.forname () لتحميل الفئات التي نحتاجها. على سبيل المثال ، الشيء الأكثر شيوعًا هو أننا عندما نقوم ببرمجة JDBC ، فإننا نستخدم class.forname () لتحميل برنامج تشغيل JDBC.
جرب {return class.forname ("oracle.jdbc.driver.oracledriver") ؛} catch (classnotfoundexception e) {// skip}فلماذا عندما نستخدم class.forname () لتحميل فئة ، إذا كان لا يمكن العثور على الفئة ، فلا نزال نحاول تحميل الفئة باستخدام loader الفئة التي تم الحصول عليها بواسطة thread.currentThread.getContextLoader ()؟ على سبيل المثال ، قد نواجه الكود التالي:
حاول {return class.forname (className) ؛} catch (classnotfoundException e) {// skip} classloader ctxClassLoader = thread.currentThread (). getContextClassLoader () ؛ if (ctxClassLoader! = null) {try {clazz = ctxClassLoader.loadClass (className) ؛ } catch (classnotfoundException e) {// skip}}الجزء BOLD هنا هو استخدام المحمل الذي تم الحصول عليه بواسطة Thread.currentThread.getContextLoader () لتحميل الفصل. من الواضح أن تحميل الفئة المستخدمة عند تحميل class.forname () قد يختلف الفصل عن محمل الفئة الذي تم الحصول عليه بواسطة thread.currentThread.getContextLoader (). فلماذا يحدث الفرق؟
جافا فئة اللودر
قبل فهم سبب استخدام هذا اللوسة الفئة التي تم الحصول عليها بواسطة thread.currentThread.getContextLoader () ، دعنا أولاً نفهم محمل الفئة (classloader) المستخدم في JVM.
هناك ثلاثة أنواع من محولات الفصل في JVM افتراضيًا:
bootstrap فئة لوك
يعد Loader من فئة Loader فئة Bootstrap عبارة عن محمل فئة مدمج في JDK ، والذي يستخدم لتحميل الفئات داخل JDK. يتم استخدام محمل فئة bootstrap لتحميل الفئات أدناه $ java_home/jre/lib في JDK ، مثل الفئات في حزمة RT.JAR. يعد محمل فئة Bootstrap جزءًا من JVM ويتم كتابته بشكل عام في الكود الأصلي.
loader فئة التمديد
يستخدم Loader فئة Loader بشكل أساسي لتحميل الفئات في حزمة تمديد JDK. بشكل عام ، يتم تحميل الحزم تحت $ java_home/lib/ext من خلال هذا المحمل الفئة ، وتبدأ الفئات الموجودة تحت هذه الحزمة بشكل أساسي مع Javax.
تحميل فئة النظام
يسمى Loader فئة Loader من فئة النظام أيضًا لوادر فئة التطبيق (AppClassLoader). كما يوحي الاسم ، يتم استخدام محمل الفئة هذا لتحميل رمز التطبيق الذي يكتبه المطورون عادة. يتم استخدام محمل فئة النظام لتحميل فئات مستوى التطبيق المخزنة في مسار ClassPath.
الرمز التالي يسرد هذه اللوادر الثلاثة:
الفئة العامة mainclass {public static void main (string [] args) {system.out.println (integer.class.getClassLoader ()) ؛ system.out.println (logging.class.getClassLoader ()) ؛ System.out.println (mainclass.class.getClassLoader ()) ؛ }}حيث يمكنك الحصول على محمل فئة bootstrap إرجاع القيمة الفارغة إلى الأبد
null # bootstrap class loader sun.misc.launcherRedExtClasslassload@5e2de80c # extension class loader sun.misc.Launcher
نموذج وفد الوالدين
اللوادر الثلاثة التي تم تقديمها أعلاه ليست معزولة ، لديهم علاقة هرمية:
تعمل اللوادر الثلاثة من الدرجة الثلاثة من خلال هذه العلاقة الهرمية وهي مسؤولة عن تحميل الفصول معًا. يُطلق على النموذج الهرمي أعلاه نموذج "الوفد الأصل" لعملية تحميل الفئة. يتطلب نموذج الوفد الأصل أن يكون لدى جميع لوادر فئة محمل أصل باستثناء محمل فئة Bootstrap من المستوى الأعلى. عندما يقوم محمل الفئة بتحميل فئة ، تحقق أولاً مما إذا كانت هناك فئات في ذاكرة التخزين المؤقت التي تم تحميلها. إذا لم يكن الأمر كذلك ، يتم تفويض المحمل الأصل لتحميل الفصل أولاً. يقوم المحمل الأصل بتنفيذ نفس العمل مثل Loader Child السابق حتى يصل الطلب إلى محمل فئة Bootstrap من المستوى الأعلى. إذا لم يتمكن المحمل الأصل من تحميل الفئة المطلوبة ، فسيحاول محمل الطفل تحميل الفئة بمفرده. إنه يعمل مشابهًا لما يلي:
يمكننا استخدام الكود في ClassLoader في JDK لمعرفة تنفيذ آلية تفويض الوالدين. يتم تطبيق الرمز في classloader.loadClass ()
الفئة المدورة <؟> loadclass (اسم السلسلة ، حل منطقي) يلقي classnotfoundException متزامن (getClassLoadingLinglock (name)) {// أولاً ، تحقق مما إذا كان قد تم بالفعل تحميل الفئة <؟> c = findloadedClass (name) ؛ if (c == null) {long t0 = system.nanotime () ؛ حاول {if (parent! = null) {c = parent.loadClass (name ، false) ؛ } else {c = findBootStrapClassornull (name) ؛ }} catch (classnotfoundException e) {// classnotfoundException تم إلقاؤه إذا لم يتم العثور على فئة // من loader من فئة الوالدين غير null} if (c == null) {// إذا لم يتم العثور عليها ، فاستدعاء FindClass بالترتيب // للعثور على الفئة. طويل T1 = system.nanotime () ؛ c = findClass (name) ؛ // هذا هو محمل فئة محدد ؛ سجل الإحصائيات sun.misc.perfcounter.getParentDelegationTime (). addTime (T1 - T0) ؛ sun.misc.perfcounter.getFindClasStime (). addelapsedTimeFrom (T1) ؛ sun.misc.perfcounter.getFindClasses (). studrement () ؛ }} if (حل) {solveClass (c) ؛ } return c ؛ }ميزة واحدة لاستخدام تفويض الوالدين لتنظيم لوادر فئة هي أن تكون آمنة. إذا حددنا فئة سلسلة بأنفسنا ، نأمل أن نستبدل فئة السلسلة هذه بتنفيذ java.lang.string في Java الافتراضي.
وضعنا ملف الفئة لفئة السلسلة التي قمنا بتنفيذها في مسار ClassPath. عندما نستخدم محمل الفئة لتحميل فئة السلسلة التي قمنا بتطبيقها ، أولاً ، سيقوم محمل الفئة بتلويح الطلب إلى المحمل الأصل ، ومن خلال الطبقة حسب الطبقة ، سيقوم محمل فئة bootstrap بتحميل نوع السلسلة في حزمة RT.JAR ، ثم إرجاعه على طول الطريق. في هذه العملية ، يتجاهل Loader من الفئة فئة السلسلة التي وضعناها في ClassPath.
إذا لم يتم اعتماد آلية التفويض الأصل ، يمكن لوكيل نظام النظام العثور على ملف فئة السلسلة في مسار ClassPath وتحميله في البرنامج ، مما تسبب في كتابة تطبيق السلسلة في JDK. لذلك ، تضمن طريقة عمل لوادر الفئة هذه أن برامج Java يمكن أن تعمل بأمان وثابتة إلى حد ما.
لوادر فئة سياق الموضوع
يتحدث المذكور أعلاه عن العديد من المحتوى المرتبط برصاصات الفئة ، ولكن لا يزال لا يتحدث عن موضوع اليوم ، لوادر فئة سياق الموضوع.
في هذه المرحلة ، نعلم بالفعل أن Java توفر ثلاثة لوادر من النوع وتعمل بالتنسيق وفقًا لآلية تفويض الوالدين الصارمة. على السطح ، يبدو مثاليًا ، ولكن هذه آلية التفويض الوالد الصارم هي التي تسبب بعض القيود عند تحميل فئات.
عندما يحتاج إطار العمل الأساسي الخاص بنا إلى استخدام فئات مستوى التطبيق ، يمكننا فقط استخدام هذه الفئات إذا تم تحميل هذه الفئة عندما يمكن تحميل محمل الفئة المستخدمة بواسطة إطار عملنا الحالي. بمعنى آخر ، لا يمكننا استخدام محمل الفئة لعملية تحميل الفئة الحالية. يحدث هذا القيد بسبب آلية تفويض الوالدين ، لأن تفويض طلبات التحميل الطبقية في اتجاه واحد.
على الرغم من عدم وجود العديد من الحالات ، لا يزال هناك مثل هذا الطلب. خدمة JNDI نموذجية. يوفر JNDI واجهة للاستعلام عن موارد ، ولكن يتم تنفيذ التنفيذ المحدد من قبل الشركات المصنعة المختلفة. في هذا الوقت ، يتم تحميل رمز JNDI بواسطة محمل فئة Bootstrap JVM ، ولكن التنفيذ المحدد هو رمز آخر غير JDK الذي يوفره المستخدم ، بحيث لا يمكن تحميله إلا بواسطة محمل فئة النظام أو محمل فئة محدد من قبل المستخدم. تحت آلية تفويض الوالدين ، لا يمكن لـ JNDI الحصول على تنفيذ SPI JNDI.
لحل هذه المشكلة ، يتم تقديم لوادر فئة سياق مؤشر ترابط. SetContextClassloader () من فئة Java.lang.Thread SetContextClassLoader () (إذا لم يتم تعيينه ، فسيتم موروثه من مؤشر ترابط الأصل بشكل افتراضي. إذا لم يكن البرنامج قد قام بتعيينه ، فسيقوم بالتخلف عن محمل فئة النظام). باستخدام loader فئة سياق مؤشر ترابط ، يمكن للتطبيق تمرير محمل الفئة المستخدمة من قبل التطبيق إلى الرمز الذي يستخدم محمل الفئة العليا من خلال java.lang.thread.setContextClassLoader (). على سبيل المثال ، يمكن لخدمة JNDI أعلاه استخدام هذه الطريقة للحصول على محمل فئة يمكنه تحميل تطبيقات SPI والحصول على فئات تنفيذ SPI المطلوبة.
يمكن ملاحظة أن إدخال لوادر فئة الخيوط هو في الواقع تدمير لآلية التفويض الأصل ، ولكنه يوفر المرونة في تحميل الفصل.
حل الشكوك
بالعودة إلى البداية ، من أجل تحميل الفئات التي ينفذها المستخدمون خارج الإطار ، قد لا يتم تحميل هذه الفئات من خلال محمل الفئة المستخدمة بواسطة الإطار. من أجل تجاوز نموذج الوفد الأصل الخاص بـ Class Loader ، يتم استخدام Thread.getContextClassLoader () لتحميل هذه الفئات.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.