Ich verstehe den Unterschied zwischen Laufzeit und Kompilierungszeit und wie man zwischen beiden unterscheidet, aber ich sehe keine Notwendigkeit, zwischen Abhängigkeiten von Kompilierungszeit und Laufzeit zu unterscheiden .
Woran ich ersticke, ist Folgendes: Wie kann ein Programm zur Laufzeit nicht von etwas abhängen , von dem es während der Kompilierung abhängt? Wenn meine Java-App log4j verwendet, benötigt sie die Datei log4j.jar zum Kompilieren (mein Code integriert und ruft Mitgliedsmethoden aus log4j heraus auf) sowie zur Laufzeit (mein Code hat absolut keine Kontrolle darüber, was passiert, wenn Code in log4j vorhanden ist .jar wird ausgeführt).
Ich lese über Tools zur Abhängigkeitsauflösung wie Ivy und Maven, und diese Tools unterscheiden deutlich zwischen diesen beiden Arten von Abhängigkeiten. Ich verstehe die Notwendigkeit einfach nicht.
Kann jemand eine einfache Erklärung vom Typ "King's English" geben, vorzugsweise mit einem tatsächlichen Beispiel, das selbst ein armer Trottel wie ich verstehen könnte?
quelle
Antworten:
Zur Laufzeit ist in der Regel eine Abhängigkeit zur Kompilierungszeit erforderlich. In maven
compile
wird dem Klassenpfad zur Laufzeit eine Abhängigkeit mit Gültigkeitsbereich hinzugefügt (z. B. werden sie in Kriegen nach WEB-INF / lib kopiert).Es ist jedoch nicht unbedingt erforderlich; Zum Beispiel können wir gegen eine bestimmte API kompilieren, was sie zu einer Abhängigkeit zur Kompilierungszeit macht, aber zur Laufzeit eine Implementierung einschließen, die auch die API enthält.
Es kann Randfälle geben, in denen das Projekt eine bestimmte Abhängigkeit zum Kompilieren erfordert, der entsprechende Code jedoch nicht benötigt wird, diese sind jedoch selten.
Andererseits ist es sehr häufig, Laufzeitabhängigkeiten einzuschließen, die zur Kompilierungszeit nicht benötigt werden. Wenn Sie beispielsweise eine Java EE 6-Anwendung schreiben, kompilieren Sie mit der Java EE 6-API. Zur Laufzeit kann jedoch jeder Java EE-Container verwendet werden. Es ist dieser Container, der die Implementierung bereitstellt.
Abhängigkeiten zur Kompilierungszeit können durch Reflektion vermieden werden. Beispielsweise kann ein JDBC-Treiber mit a
Class.forName
geladen werden und die tatsächlich geladene Klasse kann über eine Konfigurationsdatei konfiguriert werden.quelle
provided
Bereich fügt eine Abhängigkeit zur Kompilierungszeit hinzu, ohne eine Laufzeitabhängigkeit hinzuzufügen, in der Erwartung, dass die Abhängigkeit zur Laufzeit auf andere Weise bereitgestellt wird (z. B. eine gemeinsam genutzte Bibliothek im Container).runtime
Auf der anderen Seite wird eine Laufzeitabhängigkeit hinzugefügt, ohne dass dies zu einer Abhängigkeit zur Kompilierungszeit wird.Jede Maven-Abhängigkeit hat einen Bereich, der definiert, auf welchem Klassenpfad diese Abhängigkeit verfügbar ist.
Wenn Sie eine JAR für ein Projekt erstellen, werden Abhängigkeiten nicht mit dem generierten Artefakt gebündelt. Sie werden nur zur Kompilierung verwendet. (Sie können Maven jedoch weiterhin dazu bringen, die Abhängigkeiten in das erstellte JAR aufzunehmen, siehe: Einschließen von Abhängigkeiten in ein JAR mit Maven )
Wenn Sie Maven zum Erstellen einer WAR- oder EAR-Datei verwenden, können Sie Maven so konfigurieren, dass Abhängigkeiten mit dem generierten Artefakt gebündelt werden, und Sie können es auch so konfigurieren, dass bestimmte Abhängigkeiten mithilfe des bereitgestellten Bereichs aus der WAR-Datei ausgeschlossen werden.
Der am häufigsten verwendete Bereich - Kompilierungsbereich - gibt an, dass die Abhängigkeit für Ihr Projekt vom Kompilierungsklassenpfad, den Unit-Test-Kompilierungs- und Ausführungsklassenpfaden und dem eventuellen Laufzeitklassenpfad verfügbar ist, wenn Sie Ihre Anwendung ausführen. In einer Java EE-Webanwendung bedeutet dies, dass die Abhängigkeit in Ihre bereitgestellte Anwendung kopiert wird. In einer JAR-Datei werden Abhängigkeiten jedoch nicht in den Kompilierungsbereich aufgenommen.
Laufzeitumfang gibt an, dass die Abhängigkeit für Ihr Projekt von den Klassenpfaden für die Ausführung von Komponententests und die Ausführung zur Laufzeit verfügbar ist. Im Gegensatz zum Kompilierungsbereich ist sie jedoch nicht verfügbar, wenn Sie Ihre Anwendung oder ihre Komponententests kompilieren . Eine Laufzeitabhängigkeit wird in Ihre bereitgestellte Anwendung kopiert, ist jedoch während der Kompilierung nicht verfügbar! Dies ist gut, um sicherzustellen, dass Sie nicht fälschlicherweise von einer bestimmten Bibliothek abhängig sind.
Schließlich gibt Provided Scope an, dass der Container, in dem Ihre Anwendung ausgeführt wird, die Abhängigkeit in Ihrem Namen bereitstellt. In einer Java EE-Anwendung bedeutet dies, dass sich die Abhängigkeit bereits im Klassenpfad des Servlet-Containers oder Anwendungsservers befindet und nicht in Ihre bereitgestellte Anwendung kopiert wird. Dies bedeutet auch, dass Sie diese Abhängigkeit zum Kompilieren Ihres Projekts benötigen.
quelle
Sie benötigen zur Kompilierungszeit Abhängigkeiten, die Sie möglicherweise zur Laufzeit benötigen. Viele Bibliotheken werden jedoch ohne alle möglichen Abhängigkeiten ausgeführt. dh eine Bibliothek, die vier verschiedene XML-Bibliotheken verwenden kann, aber nur eine benötigt, um zu funktionieren.
Viele Bibliotheken benötigen wiederum andere Bibliotheken. Diese Bibliotheken werden zur Kompilierungszeit nicht benötigt, aber zur Laufzeit. dh wenn der Code tatsächlich ausgeführt wird.
quelle
Im Allgemeinen haben Sie Recht und wahrscheinlich ist es die ideale Situation, wenn Laufzeit- und Kompilierungszeitabhängigkeiten identisch sind.
Ich werde Ihnen 2 Beispiele geben, wenn diese Regel falsch ist.
Wenn Klasse A von Klasse B abhängt, die von Klasse C abhängt, die von Klasse D abhängt, wobei A Ihre Klasse ist und B, C und D Klassen aus verschiedenen Bibliotheken von Drittanbietern sind, benötigen Sie zur Kompilierungszeit nur B und C und Sie benötigen auch D um Laufzeit. Oft verwenden Programme das dynamische Laden von Klassen. In diesem Fall benötigen Sie keine Klassen, die dynamisch von der Bibliothek geladen werden, die Sie zur Kompilierungszeit verwenden. Darüber hinaus wählt die Bibliothek häufig aus, welche Implementierung zur Laufzeit verwendet werden soll. Beispielsweise können SLF4J oder Commons Logging die Zielprotokollimplementierung zur Laufzeit ändern. Zum Kompilieren benötigen Sie nur SSL4J.
Gegenüberliegendes Beispiel, wenn Sie zur Kompilierungszeit mehr Abhängigkeiten benötigen als zur Laufzeit. Denken Sie, dass Sie eine Anwendung entwickeln, die in verschiedenen Umgebungen oder Betriebssystemen funktionieren muss. Sie benötigen alle plattformspezifischen Bibliotheken zur Kompilierungszeit und nur Bibliotheken, die zur Laufzeit für die aktuelle Umgebung benötigt werden.
Ich hoffe meine Erklärungen helfen.
quelle
Normalerweise ist das statische Abhängigkeitsdiagramm ein Unterdiagramm des dynamischen, siehe z. B. diesen Blogeintrag des Autors von NDepend .
Es gibt jedoch einige Ausnahmen, hauptsächlich Abhängigkeiten, die Compiler-Unterstützung hinzufügen, die zur Laufzeit unsichtbar wird. Zum Beispiel für die Codegenerierung über Lombok oder zusätzliche Prüfungen wie über das (steckbare Typ-) Checker Framework .
quelle
Ich bin gerade auf ein Problem gestoßen, das Ihre Frage beantwortet.
servlet-api.jar
ist eine vorübergehende Abhängigkeit in meinem Webprojekt und wird sowohl zur Kompilierungszeit als auch zur Laufzeit benötigt. Aberservlet-api.jar
aber auch in meiner Tomcat-Bibliothek enthalten.Die Lösung besteht darin,
servlet-api.jar
in maven nur zur Kompilierungszeit verfügbar zu machen und nicht in meine Kriegsdatei zu packen, damit es nicht mit denservlet-api.jar
in meiner Tomcat-Bibliothek enthaltenen kollidiert .Ich hoffe, dies erklärt die Abhängigkeit von Kompilierungszeit und Laufzeit.
quelle
compile
undprovided
Bereichen und nicht zwischencompile
und erklärtruntime
.Compile scope
wird sowohl zur Kompilierungszeit benötigt als auch in Ihrer App gepackt.Provided scope
wird nur zur Kompilierungszeit benötigt, ist aber nicht in Ihrer App gepackt, da es von anderen Anbietern bereitgestellt wird, z. B. bereits auf dem Tomcat-Server.compile
undruntime
maven Bereiche . Derprovided
Bereich ist die Art und Weise, wie Maven den Fall behandelt, in dem eine Abhängigkeit zur Kompilierungszeit nicht im Laufzeitpaket enthalten sein sollte.Die allgemeinen Konzepte zur Kompilierungs- und Laufzeit sowie die Maven-spezifischen
compile
undruntime
Bereichsabhängigkeiten sind zwei sehr unterschiedliche Dinge. Sie können sie nicht direkt vergleichen, da diese nicht denselben Rahmen haben: Die allgemeinen Kompilierungs- und Laufzeitkonzepte sind breit gefächert, während es bei den Maven-compile
undruntime
Scope-Konzepten speziell um die Verfügbarkeit / Sichtbarkeit von Abhängigkeiten je nach Zeit geht: Kompilierung oder Ausführung.Vergessen Sie nicht, dass Maven vor allem ein
javac
/java
wrapper ist und dass Sie in Java einen Klassenpfad zur Kompilierungszeit haben, mit dem Sie angeben,javac -cp ...
und einen Laufzeitklassenpfad, mit dem Sie angebenjava -cp ...
.Es wäre nicht falsch, den Maven-
compile
Bereich als eine Möglichkeit zu betrachten, eine Abhängigkeit sowohl in der Java-Kompilierung als auch im Laufzeitklassenpfad hinzuzufügen (javac
undjava
) während der Maven-runtime
Bereich als eine Möglichkeit angesehen werden kann, eine Abhängigkeit nur im Java-Laufzeitklassenpfad (javac
) hinzuzufügen .Was Sie beschreiben, hat keine Beziehung zu
runtime
undcompile
Umfang.Es sieht eher nach dem
provided
Bereich aus, den Sie angeben, damit eine Abhängigkeit zur Kompilierungszeit, jedoch nicht zur Laufzeit davon abhängt.Sie verwenden es, wenn Sie die Abhängigkeit zum Kompilieren benötigen, möchten es jedoch nicht in die gepackte Komponente (JAR, WAR oder andere) aufnehmen, da die Abhängigkeit bereits von der Umgebung bereitgestellt wird: Sie kann auf dem Server oder in einer anderen Komponente enthalten sein Pfad des Klassenpfads, der beim Starten der Java-Anwendung angegeben wurde.
In diesem Fall ja. Angenommen, Sie müssen einen tragbaren Code schreiben, der auf slf4j als Fassade vor log4j basiert, um später zu einer anderen Protokollierungsimplementierung wechseln zu können (log4J 2, logback oder eine andere).
In diesem Fall müssen Sie in Ihrem pom slf4j als
compile
Abhängigkeit angeben (dies ist die Standardeinstellung), aber Sie geben die log4j-Abhängigkeit alsruntime
Abhängigkeit an:Auf diese Weise konnten die log4j-Klassen im kompilierten Code nicht referenziert werden, Sie können jedoch weiterhin auf slf4j-Klassen verweisen.
Wenn Sie die beiden Abhängigkeiten mit der
compile
Zeit angegeben haben, hindert Sie nichts daran, log4j-Klassen im kompilierten Code zu referenzieren, und Sie könnten so eine unerwünschte Kopplung mit der Protokollierungsimplementierung erstellen:Eine häufige Verwendung des
runtime
Gültigkeitsbereichs ist die JDBC-Abhängigkeitsdeklaration. Um portablen Code zu schreiben, möchten Sie nicht, dass der Clientcode auf Klassen der spezifischen DBMS-Abhängigkeit verweist (z. B. PostgreSQL JDBC-Abhängigkeit), aber Sie möchten, dass er trotzdem in Ihre Anwendung aufgenommen wird, da zur Laufzeit die Klassen erstellt werden müssen Die JDBC-API funktioniert mit diesem DBMS.quelle
Zur Kompilierungszeit aktivieren Sie Verträge / APIs, die Sie von Ihren Abhängigkeiten erwarten. (zB: hier unterschreiben Sie nur einen Vertrag mit dem Breitband-Internetprovider) Zur Laufzeit nutzen Sie tatsächlich die Abhängigkeiten. (zB: hier nutzen Sie tatsächlich das Breitband-Internet)
quelle
Um die Frage zu beantworten, "wie kann ein Programm zur Laufzeit nicht von etwas abhängen, von dem es während der Kompilierung abhängt?", Schauen wir uns das Beispiel eines Annotationsprozessors an.
Angenommen, Sie haben Ihren eigenen Anmerkungsprozessor geschrieben und nehmen an
com.google.auto.service:auto-service
, dass er von der Kompilierungszeit abhängig ist, damit er verwendet werden kann@AutoService
. Diese Abhängigkeit ist nur erforderlich , die Anmerkung Prozessor zu kompilieren, aber es ist nicht zur Laufzeit benötigt: alle anderen Projekte abhängig von Annotations - Prozessor für Anmerkungen Verarbeitung nicht nicht auf die Abhängigkeit benötigtcom.google.auto.service:auto-service
zur Laufzeit (noch zur Compile-Zeit noch zu einem anderen Zeitpunkt) .Dies ist nicht sehr häufig, aber es passiert.
quelle
Der
runtime
Bereich soll verhindern, dass Programmierer direkte Abhängigkeiten zu Implementierungsbibliotheken im Code hinzufügen, anstatt Abstraktionen oder Fassaden zu verwenden.Mit anderen Worten, es wird die Verwendung von Schnittstellen erzwungen.
Konkrete Beispiele:
1) Ihr Team verwendet SLF4J über Log4j. Sie möchten, dass Ihre Programmierer die SLF4J-API verwenden, nicht die Log4j-API. Log4j darf nur intern von SLF4J verwendet werden. Lösung:
2) Ihre Anwendung greift über JDBC auf MySQL zu. Sie möchten, dass Ihre Programmierer gegen die Standard-JDBC-Abstraktion codieren, nicht direkt gegen die MySQL-Treiberimplementierung.
mysql-connector-java
(MySQL JDBC-Treiber) als Laufzeitabhängigkeit.Laufzeitabhängigkeiten werden während der Kompilierung ausgeblendet (Fehler beim Kompilieren werden ausgelöst, wenn Ihr Code eine "direkte" Abhängigkeit von ihnen aufweist), werden jedoch während der Ausführungszeit und beim Erstellen von bereitstellbaren Artefakten (WAR-Dateien, SHADED-JAR-Dateien usw.) berücksichtigt.
quelle