ملاحظة: الكلمات التالية مثل "الجسر" و "التحويل" و "الربط" هي في الأساس نفس المفهوم.
log4j-over-SLF4J و SLF4J-LOG4J12 هما حزمتان مرتبطتان بنظام سجل Java. عندما تظهر تحت classpath في نفس الوقت ، قد تتسبب في استثناءات فائض في التدفق. معلومات الاستثناء تقريبًا على النحو التالي (المقتطف من وثيقة الموقع الرسمي SLF4J المكتشفة على حد سواء log4j-over-slf4j.jar و slf4j-log4j12.jar على مسار الفصل ، preprespting stackoverflowerror):
استثناء في الموضوع "الرئيسي" java.lang.stackoverflowror
في java.util.hashtable.containskey (hashtable.java:306)
at org.apache.log4j.log4jloggerfactory.getLogger (log4jloggerfactory.java:36)
في org.apache.log4j.logmanager.getLogger (logmanager.java:39)
في org.slf4j.impl.log4jloggerfactory.getLogger (log4jloggerfactory.java:73)
في org.slf4j.loggerfactory.getLogger (loggerfactory.java:249)
at org.apache.log4j.category. <init> (الفئة. java:53)
at org.apache.log4j.logger .. <Ing> (logger.java:35)
at org.apache.log4j.log4jloggerfactory.getLogger (log4jloggerfactory.java:39)
في org.apache.log4j.logmanager.getLogger (logmanager.java:39)
في org.slf4j.impl.log4jloggerfactory.getLogger (log4jloggerfactory.java:73)
في org.slf4j.loggerfactory.getLogger (loggerfactory.java:249)
at org.apache.log4j.category .. <init> (الفئة. java:53)
at org.apache.log4j.logger .. <Ing> (logger.java:35)
at org.apache.log4j.log4jloggerfactory.getLogger (log4jloggerfactory.java:39)
في org.apache.log4j.logmanager.getLogger (logmanager.java:39)
تم حذف الخطوط اللاحقة ...
نظام التسجيل الحالي
قبل تحليل الأسباب المحددة لهذا الاستثناء ، من الضروري فهم نظام تسجيل Java الحالي بسرعة. الشكل التالي هو مخطط لنظام تسجيل Java الحالي:
الصورة أعلاه ليست دقيقة للغاية ، ولكن يمكنها عرض الهيكل الرئيسي لنظام سجل Java الحالي بوضوح. يمكن تقسيم نظام تسجيل Java تقريبًا إلى ثلاثة أجزاء: تطبيق واجهة السجل ، والجسر ، وتطبيق إطار عمل محدد.
هناك العديد من أنواع أطراف تسجيل Java. أبسط واحد هو Java.Util.logging الخاص بـ Java ، والأكثر كلاسيكية هو Log4J. في وقت لاحق ، ظهر تسجيل لأداء أفضل من LOG4J ، لذلك لا يتم استخدام أطراف السجل الأخرى بشكل شائع. من المؤكد أنه من الممكن للتطبيقات استخدام واجهات برمجة التطبيقات الخاصة بأطر السجل المحددة هذه لتلبية متطلبات إخراج السجل ، ولكن نظرًا لأن واجهات برمجة التطبيقات بين كل إطار سجل عادة ما تكون غير متوافقة ، فإن القيام بذلك يجعل التطبيق يفقد مرونة استبدال إطار السجل.
خيار أكثر منطقية من استخدام واجهة برمجة تطبيقات Log Framework مباشرة هو استخدام واجهة واجهة السجل. توفر واجهة واجهة السجل مجموعة من واجهات برمجة التطبيقات المستقلة عن أطر السجل المحددة. يمكن للتطبيقات فصلها من أطر عمل محددة باستخدام واجهات برمجة التطبيقات المستقلة هذه ، والتي تشبه JDBC. كانت الواجهة الأولى للواجهة السجل هي غطس العموم ، ولكن الواجهة الأكثر شعبية في الوقت الحالي هي SLF4J.
عادةً لا تحتوي واجهة واجهة السجل نفسها على إمكانية إخراج السجل الفعلية. لا يزال يحتاج إلى استدعاء واجهة برمجة تطبيقات إطار عمل محددة في الأسفل ، أي أنه في الواقع يجب استخدامه مع إطار السجل المحدد. نظرًا لوجود العديد من أطر السجل المحددة ومعظمها غير متوافقة مع بعضها البعض ، إذا تم الجمع بين واجهة واجهة السجل مع أي إطار عمل ، فقد يتطلب ذلك جسرًا مقابلًا ، تمامًا مثلما يتطلب المزيج بين JDBC وقواعد البيانات المختلفة المختلفة برنامج تشغيل JDBC المقابل.
تجدر الإشارة إلى أنه كما ذكرنا سابقًا ، فإن الصورة أعلاه ليست دقيقة ، وهذا هو الجزء الرئيسي فقط ، والوضع الفعلي ليس دائمًا خطًا بسيطًا في اتجاه واحد من "واجهة السجل واجهة-جسر-> إطار السجل". في الواقع ، ليست هناك حاجة للجسور المستقلة في بعض الأحيان ، وليس مجرد جسر يحول واجهة برمجة تطبيقات واجهة السجل إلى واجهة برمجة تطبيقات إطار عمل محددة. هناك أيضًا جسور تقوم بتحويل واجهة برمجة تطبيقات LOG Framework إلى واجهة برمجة تطبيقات واجهة السجل.
بعبارة صريحة ، فإن ما يسمى "الجسر" ليس أكثر من تطبيق زائف لمجموعة معينة من واجهات برمجة التطبيقات. لا يكمل هذا التنفيذ مباشرة الوظائف التي أعلنها واجهة برمجة التطبيقات ، ولكنها تستدعي واجهات برمجة التطبيقات الأخرى مع وظائف مماثلة. هذا يكمل الانتقال من "مجموعة من واجهات برمجة التطبيقات" إلى "واجهات برمجة التطبيقات الأخرى". إذا كان هناك جسران ، A-to-B.JAR و B-to-A.Jar ، فيمكنك أن تتخيل ما سيحدث عندما يبدأ التطبيق في استدعاء واجهة برمجة التطبيقات A أو B. هذا هو المبدأ الأساسي لاستثناء الفائض المكدس الذي تم تقديمه لأول مرة.
SLF4J إعادة توجيه الربط
ما سبق هو مجرد تفسير عام لنظام تسجيل Java الحالي ، وليس من الممكن شرح المشكلة بالتفصيل. نحتاج إلى زيادة فهم موقف التجسير بين SLF4J وإطار السجل المحدد.
SLF4J الجسور إلى إطار سجل محدد
الصورة التالية تأتي من وثيقة الموقع الرسمي SLF4J ملزمة مع إطار تسجيل في وقت النشر:
يمكنك أن ترى أن هناك العديد من الحلول لـ SLF4J للجمع مع أطر سجل محددة. بالطبع ، يتم توحيد الطبقة العلوية (طبقة التطبيق الخضراء) لكل حل. يطلقون مباشرة على واجهة برمجة التطبيقات التي توفرها SLF4J (طبقة API الزرقاء الفاتحة) والاعتماد على SLF4J-API.JAR. ثم ، إذا قمت بعمل SLF4J API لأسفل ، فسيكون ذلك مجانيًا للغاية ، ويمكنك استخدام جميع أطر التسجيل المحددة تقريبًا. لاحظ أن الطبقة الثانية في الشكل زرقاء فاتحة. بالنظر إلى الأسطورة في الزاوية اليسرى السفلى ، يمكننا أن نرى أن هذا يمثل واجهة برمجة تطبيقات السجل التجريدي ، مما يعني أنها ليست تطبيقات ملموسة. إذا لم يتم دمج الطبقة السفلية مع أي تطبيق إطار عمل محدد مثل الحل الأول على اليسار ، فلا يمكن إخراج السجل ( ليس من المؤكد ما إذا كان قد يكون الإخراج إلى الإخراج القياسي افتراضيًا ).
من الواضح أن الطبقة الثالثة في الصورة ليست أنيقة مثل الطبقات الأولى والثانية ، لأن إطار السجل المحدد بدأ يشارك هنا.
أولاً ، دعونا نلقي نظرة على كتلتي بحيرة الأزرق في منتصف الطابق الثالث ، وهي طبقة المحول ، أي الجسر. يمكن لجسر SLF4J-LOG4J122.JAR على اليسار معرفة أنه جسر من SLF4J إلى LOG4J استنادًا إلى الاسم. وبالمثل ، فإن slf4j-jdk14.jar على اليمين هو جسر تم تنفيذه بواسطة SLF4J إلى سجلات Java الأصلية. الطبقة التالية منها هي تطبيق إطار السجل المقابل. رمز التنفيذ الخاص بـ log4j هو log4j.jar ، ويتم تضمين رمز تنفيذ JUL بالفعل في وقت تشغيل JVM ولا يتطلب حزمة جرة منفصلة.
دعونا نلقي نظرة على الكتل الأزرق الداكنة الثلاث المتبقية في الطابق الثالث. الثلاثة منهم هم أيضًا تطبيقات إطار تسجيل محددة ، لكنهم لا يحتاجون إلى الجسور لأنهم ينفذون بشكل مباشر واجهة برمجة تطبيقات SLF4J. وغني عن القول ، slf4j-simple.jar و slf4j-nop.jar ، يمكنك معرفة أن أحدهم هو تطبيق بسيط لـ SLF4J والآخر هو تطبيق فارغ لـ SLF4J ، وهو ليس مفيدًا جدًا في الأوقات العادية. يُقال إن السبب وراء قيام Logback أيضًا بتنفيذ SLF4J API بسبب أن Logback و SLF4J من نفس الشخص ، وهو أيضًا مؤلف Log4J.
تحتوي جميع حزم الجرة الرمادية في الطبقة الثالثة على صناديق حمراء ، مما يعني أنها جميعها تنفذ مباشرة واجهة برمجة تطبيقات SLF4J. ومع ذلك ، فإن تنفيذ API SLF4J بواسطة Lake Blue Bridge لا يخرج بشكل مباشر سجلات ، ولكنه بدلاً من ذلك يدعو واجهة برمجة تطبيقات أطر السجل الأخرى.
مكالمات أخرى من إطار السجل API مرة أخرى إلى SLF4J
إذا كانت الجسور المذكورة أعلاه فقط من SFL4J إلى أطر السجل الأخرى موجودة ، فقد لا تكون هناك أي مشاكل. ولكن في الواقع ، هناك نوع آخر من الجسر ، وظائفه هي بالضبط عكس ما ورد أعلاه ، فإنها تقوم بتحويل واجهة برمجة تطبيقات أطر السجل الأخرى إلى API SLF4J. الصورة التالية تأتي من وثيقة وثيقة الموقع الرسمي لـ SLF4J:
يوضح الشكل أعلاه جميع الحالات الثلاث التي يمكنها الاتصال بأمان إلى SLF4J من واجهات برمجة التطبيقات الإطارية الأخرى حتى الآن.
أخذ الحالة الأولى في الزاوية اليسرى العليا كمثال ، عندما يتم سد SLF4J الأساسي إلى إطار Logback ، فإن واجهات برمجة تطبيقات إطار السجل التي تسمح للطبقة العليا بالرجوع إلى SLF4J تشمل Log4J و Jul. على الرغم من أن JCL ليس تطبيقًا محددًا لإطار تسجيل ، إلا أنه لا يزال من الممكن استدعاء API إلى SLF4J. لتنفيذ التحويل ، تتمثل الطريقة في استبدال جرة إطار السجل الأصلي بجرة جسر محددة مدرجة في الشكل. تجدر الإشارة إلى أن واجهة برمجة تطبيقات LOGBACK إلى API SLF4J لا تتضمن تحويل واجهة برمجة تطبيقات LOGBACK ، لأن LOGBACK هو في الأصل تطبيق SLF4J API.
بعد قراءة المواقف الثلاثة ، ستجد أنه يمكن إعادة توجيه جميع واجهات برمجة التطبيقات لأطر السجل الأخرى ، بما في ذلك API JCL ، إلى SLF4J في الإرادة. ولكن هناك قيود فقط على أن إطار السجل الذي يستدعي مرة أخرى إلى SLF4J لا يمكن أن يكون هو نفسه إطار السجل الذي يقوم به SLF4J حاليًا. هذا القيد هو منع A-to-B.JAR و B-to-A.Jar من الظهور في الفئة في نفس الوقت ، مما يؤدي إلى استدعاء A و B باستمرار مع بعضهما البعض بشكل متكرر ، وأخيراً فائض المكدس. في الوقت الحاضر ، لا يضمن هذا التقييد التكنولوجيا ، ولكن فقط من قبل المطور نفسه. هذا هو السبب في أن الموقع الرسمي لـ SLF4J يؤكد أن جميع الأساليب المعقولة هي فقط في المواقف الثلاثة في الصورة أعلاه.
في هذه المرحلة ، كان مبدأ الاستثناء الموضح في البداية واضحًا بشكل أساسي. بالإضافة إلى ذلك ، من الشكل أعلاه ، يمكننا أن نرى أنه قد تكون هناك مجموعات مشابهة للاستثناءات ليس فقط log4j-over-slf4j و slf4j-log4j12. أشار الموقع الرسمي لـ SLF4J أيضًا إلى زوج آخر: JCL-Over-SLF4J.JAR و SLF4J-JCL.JAR.
مثال رمز
التحليل السابق نظري. حتى إذا تم استخدام log4j-over-slf4j و slf4j-log4j12 في نفس الوقت في الكود الفعلي ، فقد لا تحدث الاستثناءات بالضرورة. يستدعي الكود التالي واجهة برمجة تطبيقات SLF4J لإخراج السجل ، ويتم سد SLF4J إلى log4j:
اختبار الحزمة ؛ الطبقة العامة HelloWorld {public static void main (string [] args) {org.apache.log4j.basicConfigurator.Configure () ؛ org.slf4j.logger logger = org.slf4j.loggerfactory.getlogger (helloworld.class) ؛قم بتكوين ClassPath لتكوين حزمة JAR (لاحظ أن Log4J قبل Log4J-Over-SLF4J):
في هذه الحالة ، يمكن لتشغيل برنامج الاختبار إخراج السجل بشكل طبيعي ولن يكون هناك استثناء فائض في التدفق. ولكن إذا قمت بضبط ترتيب الجرة على classpath إلى:
سيؤدي تشغيل برنامج الاختبار مرة أخرى إلى استثناء فائض مكدس على غرار التدفق الأولي في هذه المقالة. يمكنك رؤية التكرارات الدورية الواضحة:
تحليل مخطط التسلسل
الصورة أعلاه هي مخطط عمود برنامج مكالمات مفصل لـ Stack Overflow. بدءًا من الاتصال 1 ، اتصل على 1.1 ، 1.1.1 ... أخيرًا ، عندما تصل إلى 1.1.1.1.1 (آخر مكالمة في الشكل) ، فقد وجد أنه هو نفس 1 مثل 1 ، وبالتالي يتم تكرار العملية اللاحقة تمامًا.
تجدر الإشارة إلى أن الصمام الأولي ليس loggerFactory.getLogger () الموضح في الشكل. هناك العديد من المكالمات المباشرة الأخرى في التطبيق التي يمكن أن تؤدي إلى استثناءات فائض مكدس. على سبيل المثال ، في رمز المثال السابق ، فإن العبارة الأولى org.apache.log4j.basicConfigurator.Configure () هي في الواقع العبارة الأولى org.apache.log4j.basicConfigurator.Configure () ، ولكن عملية الاتصال المتداولة المتبادلة هي نفس الشكل السابق.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.