Hinweis: Die folgenden Wörter wie "Brücke", "Konvertierung" und "Bindung" sind im Grunde das gleiche Konzept.
Log4J-Over-SLF4J und SLF4J-Log4J12 sind zwei JAR-Pakete, die sich auf das Java-Protokollsystem beziehen. Wenn sie gleichzeitig unter dem Klassenpfad erscheinen, können sie Stapelüberlaufausnahmen verursachen. Die Ausnahmeinformationen sind ungefähr wie folgt (aus dem offiziellen SLF4J-Website-Dokument ausgewählt, sowohl log4j-over-slf4j.jar als auch slf4j-log4j12.jar auf dem Klassenpfad, Preeming Stackoverflowerror) festgestellt:
Ausnahme in Thread "Haupt" java.lang.stackoverflowerror
bei java.util.hashtable.containsKey (Hashtable.java:306)
at org.apache.log4j.log4jloggerfactory.getLogger (log4jloggerfactory.java:36)
bei org.apache.log4j.logmanager.getlogger (logManager.java:39)
bei org.slf4j.impl.log4jloggerfactory.getLogger (log4jloggerfactory.java:73)
bei org.slf4j.loggerfactory.getLogger (loggerfactory.java:249)
bei org.apache.log4j.category. <init> (category.java:53)
bei org.apache.log4j.logger .. <init> (logger.java:35)
at org.apache.log4j.log4jloggerfactory.getLogger (log4jloggerfactory.java:39)
bei org.apache.log4j.logmanager.getlogger (logManager.java:39)
bei org.slf4j.impl.log4jloggerfactory.getLogger (log4jloggerfactory.java:73)
bei org.slf4j.loggerfactory.getLogger (loggerfactory.java:249)
bei org.apache.log4j.category .. <init> (category.java:53)
bei org.apache.log4j.logger .. <init> (logger.java:35)
at org.apache.log4j.log4jloggerfactory.getLogger (log4jloggerfactory.java:39)
bei org.apache.log4j.logmanager.getlogger (logManager.java:39)
nachfolgende Linien weggelassen ...
Vorhandenes Protokollierungssystem
Vor der Analyse der spezifischen Gründe für diese Ausnahme ist es erforderlich, das vorhandene Java -Protokollierungssystem schnell zu verstehen. Die folgende Abbildung ist ein Diagramm des vorhandenen Java -Protokollierungssystems:
Das obige Bild ist nicht sehr genau, kann jedoch die Hauptstruktur des vorhandenen Java -Protokollsystems deutlich anzeigen. Das Java -Protokollierungssystem kann grob in drei Teile unterteilt werden: Log -Fassadenschnittstelle, Brücke und Protokoll -Frameworkspezifische Implementierung.
Es gibt viele Arten von Java -Protokollierungsrahmen. Das einfachste ist Javas eigener Java.util.Logging, und das klassischste ist Log4j. Später erschien ein Logback mit einer besseren Leistung als log4j, sodass andere Protokoll -Frameworks nicht sehr häufig verwendet werden. Es ist sicherlich möglich, dass Anwendungen die APIs dieser spezifischen Protokoll -Frameworks direkt verwenden, um die Anforderungen an die Protokollausgabe zu erfüllen. Da die APIs zwischen jedem Protokoll -Framework jedoch normalerweise nicht kompatibel sind, verliert die Anwendung die Flexibilität des Austauschs des Protokoll -Frameworks.
Eine vernünftigere Option als die direkte Verwendung der spezifischen Protokoll -Framework -API besteht darin, die Schnittstelle zur Protokollfassade zu verwenden. Die Log -Fassadenschnittstelle bietet eine Reihe von APIs, die unabhängig von bestimmten Protokoll -Frameworks sind. Anwendungen können mithilfe dieser unabhängigen APIs von bestimmten Protokoll -Frameworks entkoppeln, die JDBC ähnlich sind. Die früheste Log-Fassaden-Schnittstelle war das Loggen von Commons, aber die beliebteste ist derzeit SLF4J.
Die Log -Fassadenschnittstelle selbst verfügt normalerweise nicht über die tatsächliche Log -Ausgangsfunktion. Es muss noch die spezifische Log -Framework -API unten aufrufen, dh sie muss tatsächlich in Kombination mit dem spezifischen Protokoll -Framework verwendet werden. Da es viele spezifische Protokoll -Frameworks gibt und größtenteils nicht kompatibel miteinander kompatibel ist, ist möglicherweise eine entsprechende Brücke, sofern die Schnittstelle der Protokollfassade mit einem beliebigen Protokoll -Framework kombiniert wird, so wie die Kombination zwischen JDBC und verschiedenen Datenbanken einen entsprechenden JDBC -Treiber erfordert.
Es ist zu beachten, dass das obige Bild, wie bereits erwähnt, nicht genau ist, dies nur der Hauptteil ist und die tatsächliche Situation nicht immer eine einfache Einweg-Linie von "Log-Fassadenschnittstelle-> Bridge-> Log-Framework" ist. Tatsächlich werden manchmal unabhängige Brücken nicht benötigt, und es ist nicht nur eine Brücke, die die Protokollfassade -API in eine bestimmte Protokoll -Framework -API umwandelt. Es gibt auch Brücken, die die Log -Framework -API in die Protokollfassaden -API umwandeln.
Um es unverblümt auszudrücken, ist die sogenannte "Brücke" nichts weiter als eine Pseudo-Implementierung einer bestimmten Reihe von APIs. Diese Implementierung vervollständigt die von der API deklarierten Funktionen nicht direkt, sondern ruft andere APIs mit ähnlichen Funktionen auf. Dies vervollständigt den Übergang von "einer Reihe von APIs" zu "anderen APIs". Wenn es zwei Brücken gibt, a-to-b.jar und b-to-a.jar, können Sie sich vorstellen, was passieren wird, wenn die Anwendung die API von A oder B. anfängt. Dies ist das Grundprinzip der Ausnahme des Stapelüberlaufs, die erstmals eingeführt wurde.
SLF4J -Weiterleitungsbindung
Das obige ist nur eine allgemeine Erklärung des vorhandenen Java -Protokollierungssystems, und es ist nicht möglich, das Problem im Detail zu erklären. Wir müssen die Überbrückungssituation zwischen SLF4J und dem spezifischen Protokoll -Framework weiter verstehen.
SLF4J -Brücken zum bestimmten Protokollrahmen
Das folgende Bild stammt aus dem offiziellen Website -Dokument der SLF4J -Dokument mit einem Protokollierungsrahmen zur Bereitstellungszeit:
Sie können sehen, dass SLF4J viele Lösungen gibt, die Sie mit bestimmten Protokoll -Frameworks kombinieren können. Natürlich ist die oberste Schicht (grüne Anwendungsschicht) jeder Lösung einheitlich. Sie nennen die von SLF4J (hellblaue abstrakte API-Schicht) bereitgestellte API und verlassen sich auf SLF4J-API.Jar. Wenn Sie dann die SLF4J -API nach unten durchführen, ist sie sehr frei und Sie können fast alle spezifischen Protokollierungs -Frameworks verwenden. Beachten Sie, dass die zweite Schicht in der Abbildung hellblau ist. Wenn wir die Legende in der unteren linken Ecke betrachten, können wir sehen, dass dies die abstrakte Protokoll -API darstellt, was bedeutet, dass sie keine konkreten Implementierungen sind. Wenn die untere Schicht nicht mit einer spezifischen Log -Framework -Implementierung wie der ersten Lösung links kombiniert wird, kann das Protokoll nicht ausgegeben werden ( es ist nicht sicher, ob sie standardmäßig an die Standardausgabe ausgegeben werden kann ).
Die dritte Schicht im Bild ist offensichtlich nicht so ordentlich wie die ersten und zweiten Ebenen, da das spezifische Protokoll -Framework hier begonnen hat.
Schauen wir uns zunächst die beiden Seeblaublöcke in der Mitte des dritten Stocks an, die die Adapterschicht sind, dh die Brücke. Die SLF4J-Log4J12.jar-Brücke links kann erkennen, dass es sich um eine Brücke von SLF4J nach log4j handelt, basierend auf dem Namen. In ähnlicher Weise ist der SLF4J-JDK14.jar auf der rechten Seite eine von SLF4J an Java Native Logs implementierte Brücke. Die nächste Schicht von ihnen ist die entsprechende Implementierung des Protokolls -Frameworks. Der Implementierungscode von LOG4J ist log4j.jar und der JUL -Implementierungscode ist bereits in der JVM -Laufzeit enthalten und benötigt kein separates JAR -Paket.
Schauen wir uns die verbleibenden drei dunkelblauen Blöcke im dritten Stock an. Die drei sind auch spezifische Protokollierungs -Framework -Implementierungen, benötigen jedoch keine Brücken, da sie selbst die SLF4J -API direkt implementieren. Unnötig zu erwähnen, dass SLF4J-Simple.jar und Slf4j-nop.jar erkennen können, dass eine einfache Implementierung von SLF4J und der andere eine leere Implementierung von SLF4J ist, was in den normalen Zeiten nicht sehr nützlich ist. Der Grund, warum Logback auch die SLF4J -API implementiert, soll der Logback und SLF4J von derselben Person stammen, die auch der Autor von Log4J ist.
Alle grauen Jar -Pakete in der dritten Ebene haben rote Kästchen, was bedeutet, dass sie alle die SLF4J -API direkt implementieren. Die Implementierung der SLF4J -API durch die Lake Blue Bridge gibt jedoch keine direkten Protokolle aus, sondern auf die API anderer Protokoll -Frameworks.
Andere Log -Framework -API ruft zu SLF4J zurück
Wenn nur die obigen Brücken von SFL4J zu anderen Protokoll -Frameworks vorhanden sind, gibt es möglicherweise keine Probleme. Tatsächlich gibt es jedoch eine andere Art von Brücke, deren Funktionen genau das Gegenteil von dem oben genannten sind. Sie konvertieren die API anderer Log -Frameworks in die SLF4J -API. Das folgende Bild stammt von der SLF4J Official Website Dokument Bridge Bridge Legacy APIs:
Die obige Abbildung zeigt alle drei Fälle, die bisher von anderen Log -Framework -APIs zu SLF4J zurückrufen können.
Wenn Sie den ersten Fall in der oberen linken Ecke als Beispiel nehmen. Wenn der zugrunde liegende SLF4J in das Logback -Framework überbrückt wird, umfasst das Log -Framework -APIs, mit dem die obere Schicht auf SLF4J zurückbrücken kann, log4j und jul. Obwohl JCL keine spezifische Implementierung eines Protokollierungsrahmens ist, kann seine API immer noch zu SLF4J zurückgerufen werden. Um die Konvertierung zu implementieren, besteht die Methode darin, das ursprüngliche Protokollrahmen -Glas durch ein bestimmtes Brückenglas zu ersetzen, das in der Abbildung aufgeführt ist. Es ist zu beachten, dass die Logback -API zur SLF4J -API nicht die Konvertierung der Logback -API enthält, da der Logback ursprünglich eine Implementierung der SLF4J -API ist.
Nachdem Sie die drei Situationen gelesen haben, werden Sie feststellen, dass fast alle APIs anderer Protokoll -Frameworks, einschließlich der JCL -API, nach Belieben zurück zu SLF4J umgeleitet werden können. Es gibt jedoch eine einzige Einschränkung, dass das Log -Framework, das zu SLF4J zurückruft, nicht mit dem Protokoll -Framework entsprechen kann, zu dem SLF4J derzeit überbrückt. Diese Einschränkung soll verhindern, dass A-to-B.Jar und B-to-A.jar gleichzeitig im Klassenpfad erscheinen, was sich ständig gegenseitig rekursiv anruft und schließlich überlaufend ist. Gegenwärtig wird diese Einschränkung nicht durch die Technologie garantiert, sondern nur vom Entwickler selbst. Aus diesem Grund betont die offizielle Website von SLF4J, dass alle vernünftigen Methoden nur in den drei Situationen im obigen Bild liegen.
Zu diesem Zeitpunkt war das Prinzip der zu Beginn gezeigten Ausnahme im Grunde klar. Darüber hinaus können wir aus der obigen Abbildung sehen, dass es möglicherweise Kombinationen gibt wie Ausnahmen nicht nur log4j-over-SLF4J und SLF4J-Log4J12. Die offizielle Website von SLF4J wies auch auf ein anderes Paar hin: JCL-Over-SLF4J.Jar und SLF4J-JCl.jar.
Codebeispiel
Die vorherige Analyse ist theoretisch. Selbst wenn log4j-over-SLF4J und SLF4J-Log4J12 gleichzeitig im tatsächlichen Code verwendet werden, treten Ausnahmen möglicherweise nicht unbedingt auf. Der folgende Code ruft die API von SLF4J auf, um das Protokoll auszugeben, und SLF4J wird an log4j überbrückt:
Pakettest; public class helloWorld {public static void main (String [] args) {org.apache.log4j.basicconfigurator.configure (); org.slf4j.Logger logger = org.slf4j.loggerfactory.Getlogger (helloworld.class);Konfigurieren Sie den Klassenpfad, um das JAR-Paket zu konfigurieren (beachten Sie, dass log4j vor log4j-over-SLF4J vorliegt):
In diesem Fall kann das Ausführen des Testprogramms das Protokoll normal ausgeben, und es wird keine Ausnahme von Stapelüberlauf geben. Aber wenn Sie die Jar -Bestellung auf dem Klassenpfad anpassen, um:
Wenn Sie das Testprogramm erneut ausführen, wird eine Stapelüberlaufausnahme ähnlich wie beim ersten Stapelüberlauf in diesem Artikel ähnlich. Sie können offensichtliche periodische Wiederholungen sehen:
Sequenzdiagrammanalyse
Das obige Bild ist ein detailliertes Call -Programmspaltendiagramm des Stapelüberlaufs. Ab der Rufnummer 1, rufen Sie 1.1, 1.1.1 an. Schließlich wird festgestellt, dass es genau das gleiche ist, wenn es 1.1.1.1.1 (der letzte Anruf in der Abbildung) erreicht, sodass der anschließende Vorgang vollständig wiederholt wird.
Es ist zu beachten, dass die anfängliche Sicherung nicht nur loggerFactory.getLogger () ist, der in der Abbildung gezeigt ist. In der Anwendung gibt es mehrere andere direkte Aufrufe, mit denen Ausnahmen über den Stapelüberlauf auslösen können. Im vorherigen Beispielcode ist beispielsweise die erste Anweisung org.apache.log4j.basicconfigurator.configure () die erste Anweisung org.apache.log4j.basicconfigurator.configure (), aber der nachfolgende gegenseitige Infinite -Reursive -Aufrufprozess ist im Grunde genommen die vorherige Abbildung.
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, es wird für das Lernen aller hilfreich sein und ich hoffe, jeder wird Wulin.com mehr unterstützen.