Je comprends la différence entre runtime et compile-time et comment faire la différence entre les deux, mais je ne vois tout simplement pas la nécessité de faire une distinction entre les dépendances de compilation et d'exécution .
Ce dont je m'étouffe, c'est ceci: comment un programme peut-il ne pas dépendre de quelque chose à l'exécution dont il dépendait lors de la compilation? Si mon application Java utilise log4j, elle a besoin du fichier log4j.jar pour compiler (mon code s'intégrant avec et invoquant des méthodes membres depuis log4j) ainsi que runtime (mon code n'a absolument aucun contrôle sur ce qui se passe une fois le code dans log4j .jar est exécuté).
Je lis sur les outils de résolution de dépendances tels que Ivy et Maven, et ces outils font clairement la distinction entre ces deux types de dépendances. Je n'en comprends tout simplement pas la nécessité.
Quelqu'un peut-il donner une explication simple de type "King's English", de préférence avec un exemple réel que même une pauvre sève comme moi pourrait comprendre?
la source
Réponses:
Une dépendance à la compilation est généralement requise au moment de l'exécution. Dans maven, une
compile
dépendance de portée sera ajoutée au chemin de classe lors de l'exécution (par exemple, dans wars, elles seront copiées dans WEB-INF / lib).Ce n'est cependant pas strictement obligatoire; par exemple, nous pouvons compiler avec une certaine API, ce qui en fait une dépendance au moment de la compilation, mais ensuite, au moment de l'exécution, inclure une implémentation qui inclut également l'API.
Il peut y avoir des cas marginaux où le projet nécessite une certaine dépendance pour être compilé, mais le code correspondant n'est pas réellement nécessaire, mais ceux-ci seront rares.
D'un autre côté, l'inclusion de dépendances d'exécution qui ne sont pas nécessaires au moment de la compilation est très courante. Par exemple, si vous écrivez une application Java EE 6, vous compilez avec l'API Java EE 6, mais au moment de l'exécution, n'importe quel conteneur Java EE peut être utilisé; c'est ce conteneur qui fournit l'implémentation.
Les dépendances au moment de la compilation peuvent être évitées en utilisant la réflexion. Par exemple, un pilote JDBC peut être chargé avec a
Class.forName
et la classe réelle chargée peut être configurée via un fichier de configuration.la source
provided
portée ajoute une dépendance au moment de la compilation sans ajouter de dépendance à l'exécution dans l'espoir que la dépendance sera fournie à l'exécution par d'autres moyens (par exemple une bibliothèque partagée dans le conteneur).runtime
d'autre part, ajoute une dépendance d'exécution sans en faire une dépendance à la compilation.Chaque dépendance Maven a une portée qui définit le chemin de classe sur lequel cette dépendance est disponible.
Lorsque vous créez un JAR pour un projet, les dépendances ne sont pas regroupées avec l'artefact généré; ils ne sont utilisés que pour la compilation. (Cependant, vous pouvez toujours faire en sorte que maven inclue les dépendances dans le jar construit, voir: Inclure des dépendances dans un jar avec Maven )
Lorsque vous utilisez Maven pour créer un fichier WAR ou EAR, vous pouvez configurer Maven pour regrouper les dépendances avec l'artefact généré, et vous pouvez également le configurer pour exclure certaines dépendances du fichier WAR en utilisant la portée fournie.
L'étendue la plus courante - Compile Scope - indique que la dépendance est disponible pour votre projet sur le chemin de classe de compilation, les chemins de classe de compilation et d'exécution du test unitaire, et le chemin de classe d'exécution éventuel lorsque vous exécutez votre application. Dans une application Web Java EE, cela signifie que la dépendance est copiée dans votre application déployée. Cependant, dans un fichier .jar, les dépendances ne seront pas incluses dans la portée de compilation.
L'étendue d'exécution indique que la dépendance est disponible pour votre projet sur les chemins de classe d'exécution de test unitaire et d'exécution d'exécution, mais contrairement à l'étendue de compilation, elle n'est pas disponible lorsque vous compilez votre application ou ses tests unitaires. Une dépendance d'exécution est copiée dans votre application déployée, mais elle n'est pas disponible lors de la compilation! C'est bon pour vous assurer de ne pas dépendre par erreur d'une bibliothèque spécifique.
Enfin, Provided Scope indique que le conteneur dans lequel votre application s'exécute fournit la dépendance en votre nom. Dans une application Java EE, cela signifie que la dépendance est déjà sur le chemin de classe du conteneur de servlet ou du serveur d'application et n'est pas copiée dans votre application déployée. Cela signifie également que vous avez besoin de cette dépendance pour compiler votre projet.
la source
Vous avez besoin au moment de la compilation des dépendances dont vous pourriez avoir besoin au moment de l'exécution. Cependant, de nombreuses bibliothèques fonctionnent sans toutes ses dépendances possibles. c'est-à-dire une bibliothèque qui peut utiliser quatre bibliothèques XML différentes, mais n'en a besoin que d'une pour fonctionner.
De nombreuses bibliothèques ont besoin à leur tour d'autres bibliothèques. Ces bibliothèques ne sont pas nécessaires au moment de la compilation, mais sont nécessaires au moment de l'exécution. c'est-à-dire lorsque le code est réellement exécuté.
la source
En général, vous avez raison et c'est probablement la situation idéale si les dépendances d'exécution et de compilation sont identiques.
Je vais vous donner 2 exemple lorsque cette règle est incorrecte.
Si la classe A dépend de la classe B qui dépend de la classe C qui dépend de la classe D où A est votre classe et B, C et D sont des classes de différentes bibliothèques tierces, vous n'avez besoin que de B et C au moment de la compilation et vous avez également besoin de D à Durée. Les programmes utilisent souvent le chargement dynamique des classes. Dans ce cas, vous n'avez pas besoin de classes chargées dynamiquement par la bibliothèque que vous utilisez au moment de la compilation. De plus, la bibliothèque choisit souvent l'implémentation à utiliser au moment de l'exécution. Par exemple, SLF4J ou Commons Logging peut modifier l'implémentation du journal cible lors de l'exécution. Vous n'avez besoin que de SSL4J lui-même au moment de la compilation.
Exemple opposé lorsque vous avez besoin de plus de dépendances au moment de la compilation qu'au moment de l'exécution. Pensez que vous développez une application qui doit fonctionner dans différents environnements ou systèmes d'exploitation. Vous avez besoin de toutes les bibliothèques spécifiques à la plate-forme au moment de la compilation et uniquement des bibliothèques nécessaires pour l'environnement actuel au moment de l'exécution.
J'espère que mes explications vous aideront.
la source
Habituellement, le graphe des dépendances statiques est un sous-graphe du graphe dynamique, voir par exemple cette entrée de blog de l'auteur de NDepend .
Cela dit, il y a quelques exceptions, principalement des dépendances qui ajoutent le support du compilateur, qui devient invisible à l'exécution. Par exemple pour la génération de code comme via Lombok ou des vérifications supplémentaires comme via le (type enfichable-) Checker Framework .
la source
Je viens de rencontrer un problème qui répond à votre question.
servlet-api.jar
est une dépendance transitoire dans mon projet Web et est nécessaire à la fois au moment de la compilation et à l'exécution. Maisservlet-api.jar
est également inclus dans ma bibliothèque Tomcat.La solution ici est de rendre
servlet-api.jar
in maven disponible uniquement au moment de la compilation et non empaqueté dans mon fichier war afin qu'il n'entre pas en conflit avec leservlet-api.jar
contenu de ma bibliothèque Tomcat.J'espère que cela explique le temps de compilation et la dépendance à l'exécution.
la source
compile
etprovided
champs et non entrecompile
etruntime
.Compile scope
est à la fois nécessaire au moment de la compilation et est intégré à votre application.Provided scope
n'est nécessaire qu'au moment de la compilation, mais n'est pas emballé dans votre application car il est fourni par un autre moyen, par exemple, il est déjà sur le serveur Tomcat.compile
et lesruntime
étendues Maven . Laprovided
portée est la façon dont maven gère le cas où une dépendance à la compilation ne doit pas être incluse dans le package d'exécution.Les concepts généraux de compilation et d'exécution et les dépendances spécifiques à Maven
compile
et à l'runtime
étendue sont deux choses très différentes. Vous ne pouvez pas les comparer directement car ils n'ont pas le même cadre: les concepts généraux de compilation et d'exécution sont larges tandis que les concepts mavencompile
etruntime
scope concernent spécifiquement la disponibilité / visibilité des dépendances en fonction du moment: compilation ou exécution.N'oubliez pas que Maven est avant tout un
javac
/java
wrapper et qu'en Java vous avez un chemin de classe au moment de la compilation que vous spécifiez avecjavac -cp ...
et un chemin de classe d'exécution que vous spécifiez avecjava -cp ...
.Il ne serait pas faux de considérer la
compile
portée Maven comme un moyen d'ajouter une dépendance à la fois dans la compilation Java et dans le classppath d'exécution (javac
etjava
) tandis que laruntime
portée Maven peut être considérée comme un moyen d'ajouter une dépendance uniquement dans le classppath (javac
) du runtime Java .Ce que vous décrivez n'a aucun rapport avec
runtime
etcompile
portée.Cela ressemble plus à la
provided
portée que vous spécifiez pour qu'une dépendance dépende de celle au moment de la compilation, mais pas au moment de l'exécution.Vous l'utilisez car vous avez besoin de la dépendance à compiler mais vous ne voulez pas l'inclure dans le composant packagé (JAR, WAR ou tout autre) car la dépendance est déjà fournie par l'environnement: elle peut être incluse dans le serveur ou tout autre chemin du chemin d'accès aux classes spécifié au démarrage de l'application Java.
Dans ce cas oui. Mais supposons que vous ayez besoin d'écrire un code portable qui s'appuie sur slf4j comme façade devant log4j pour pouvoir passer à une autre implémentation de journalisation plus tard (log4J 2, logback ou toute autre).
Dans ce cas, dans you pom, vous devez spécifier slf4j en tant que
compile
dépendance (c'est la valeur par défaut) mais vous spécifierez la dépendance log4j en tant queruntime
dépendance:De cette façon, les classes log4j n'ont pas pu être référencées dans le code compilé mais vous pourrez toujours faire référence aux classes slf4j.
Si vous avez spécifié les deux dépendances avec l'
compile
heure, rien ne vous empêchera de référencer les classes log4j dans le code compilé et vous pourriez ainsi créer un couplage indésirable avec l'implémentation de la journalisation:La
runtime
déclaration de dépendance JDBC est une utilisation courante de la portée. Pour écrire du code portable, vous ne voulez pas que le code client puisse faire référence aux classes de la dépendance SGBD spécifique (par exemple: dépendance JDBC PostgreSQL) mais vous voulez tout de même l'inclure dans votre application car au moment de l'exécution, les classes sont nécessaires pour créer l'API JDBC fonctionne avec ce SGBD.la source
Au moment de la compilation, vous activez les contrats / API que vous attendez de vos dépendances. (par exemple: ici vous venez de signer un contrat avec un fournisseur d'accès Internet haut débit) Au moment de l'exécution, vous utilisez les dépendances. (par exemple: ici, vous utilisez effectivement Internet haut débit)
la source
Pour répondre à la question "comment un programme peut-il ne pas dépendre à l'exécution de quelque chose dont il dépendait lors de la compilation?", Regardons l'exemple d'un processeur d'annotations.
Supposons que vous ayez écrit votre propre processeur d'annotations, et supposez qu'il ait une dépendance au moment de la compilation
com.google.auto.service:auto-service
pour pouvoir l'utiliser@AutoService
. Cette dépendance n'est requise que pour compiler le processeur d'annotations, mais elle n'est pas obligatoire au moment de l'exécution: tous les autres projets dépendant de votre processeur d'annotations pour le traitement des annotations ne nécessitent pas la dépendance surcom.google.auto.service:auto-service
au moment de l'exécution (ni au moment de la compilation ni à aucun autre moment) .Ce n'est pas très courant, mais cela arrive.
la source
La
runtime
portée est là pour empêcher les programmeurs d'ajouter des dépendances directes aux bibliothèques d'implémentation dans le code au lieu d'utiliser des abstractions ou des façades.En d'autres termes, il oblige à utiliser des interfaces.
Exemples concrets:
1) Votre équipe utilise SLF4J sur Log4j. Vous voulez que vos programmeurs utilisent l'API SLF4J, pas celle de Log4j. Log4j doit être utilisé par SLF4J uniquement en interne. Solution:
2) Votre application accède à MySQL à l'aide de JDBC. Vous voulez que vos programmeurs codent par rapport à l'abstraction standard JDBC, pas directement par rapport à l'implémentation du pilote MySQL.
mysql-connector-java
(pilote MySQL JDBC) comme une dépendance d'exécution.Les dépendances d'exécution sont masquées lors de la compilation (générant des erreurs de compilation si votre code a une dépendance "directe" sur elles) mais sont incluses lors de l'exécution et lors de la création d'artefacts déployables (fichiers WAR, fichiers JAR SHADED, etc.).
la source