120 votes

Un programme peut-il dépendre d'une bibliothèque pendant la compilation mais pas pendant l'exécution ?

Je comprends la différence entre le temps d'exécution et le temps de compilation et comment faire la différence entre les deux, mais je ne vois pas la nécessité de faire une distinction entre le temps de compilation et le temps d'exécution. Dépendances .

Ce qui m'étouffe, c'est ceci : comment un programme ne pas dépendre sur quelque chose à l'exécution dont il dépendait à la compilation ? Si mon application Java utilise log4j, elle a besoin du fichier log4j.jar pour la compilation (mon code s'intègre à log4j et invoque des méthodes membres à partir de log4j) ainsi que pour l'exécution (mon code n'a absolument aucun contrôle sur ce qui se passe une fois que le code contenu dans log4j.jar est exécuté).

Je me suis renseigné 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 ne comprends tout simplement pas la nécessité de cette distinction.

Quelqu'un peut-il donner une explication simple, du type "King's English", de préférence avec un exemple concret que même un pauvre idiot comme moi pourrait comprendre ?

2 votes

Vous pouvez utiliser la réflexion, et utiliser des classes qui n'étaient pas disponibles au moment de la compilation. Pensez "plugin".

66voto

Artefacto Points 50896

Une dépendance de compilation est généralement requise au moment de l'exécution. Dans maven, une compile seront ajoutées au classpath au moment de l'exécution (par exemple, dans les guerres, elles seront copiées dans WEB-INF/lib).

Elle n'est cependant pas strictement nécessaire ; par exemple, nous pouvons compiler contre une certaine API, en en faisant une dépendance de 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 compiler mais où le code correspondant n'est pas réellement nécessaire, mais ces cas seront rares.

D'autre part, il est très courant d'inclure des dépendances d'exécution qui ne sont pas nécessaires au moment de la compilation. 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é à l'aide de la fonction Class.forName et la classe réelle chargée est configurable par le biais d'un fichier de configuration.

18 votes

À propos de l'API Java EE, n'est-ce pas à cela que sert l'étendue de la dépendance "fournie" ?

18 votes

Lombok (www.projectlombok.org) est un exemple où une dépendance est nécessaire à la compilation mais pas à l'exécution. Le jar est utilisé pour transformer le code java au moment de la compilation mais n'est pas du tout nécessaire au moment de l'exécution. En spécifiant la portée "provided", le jar ne sera pas inclus dans le war/jar.

2 votes

@Kevin Oui, bon point, la provided scope ajoute une dépendance au moment de la compilation sans ajouter de dépendance au moment de l'exécution, en espérant que la dépendance sera fournie au moment de 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 de compilation.

35voto

Koray Tugay Points 1885

Chaque dépendance de Maven a une portée qui définit sur quel classpath 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é ; elles sont utilisées uniquement pour la compilation. (Cependant, vous pouvez toujours faire en sorte que maven inclue les dépendances dans le jar construit, voir : Inclure les dépendances dans un jar avec Maven )

Lorsque vous utilisez Maven pour créer un fichier WAR ou EAR, vous pouvez configurer Maven pour qu'il regroupe les dépendances avec l'artefact généré, et vous pouvez également le configurer pour qu'il exclue certaines dépendances du fichier WAR à l'aide de la commande provided l'étendue.

Le champ d'application le plus courant - compile - indique que la dépendance est disponible pour votre projet sur le classpath de compilation, les classpaths de compilation et d'exécution des tests unitaires, et l'éventuel classpath d'exécution 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. Dans un fichier JAR cependant, les dépendances sont no être inclus lorsque le compile est utilisé.

runtime scope indique que la dépendance est disponible pour votre projet sur les classpaths d'exécution des tests unitaires et d'exécution du runtime, mais contrairement à l'indicateur compile le champ d'application 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 pendant la compilation. Cela permet de s'assurer que vous ne dépendez pas par erreur d'une bibliothèque spécifique. Imaginons que vous utilisiez une implémentation de journalisation spécifique, mais que vous souhaitiez uniquement importer une façade de journalisation dans votre code source. Vous incluriez la bibliothèque de journalisation concrète avec un runtime afin de ne pas s'y fier par erreur.

Enfin, provided 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 se trouve déjà dans le classpath du conteneur de servlets ou du serveur d'applications et que le nom de la dépendance est déjà sur la page d'accueil. n'est pas copié dans votre application déployée. Cela signifie également que vous avez besoin de cette dépendance pour compiler votre projet.

0 votes

@Koray Tugay La réponse est plus précise :) J'ai une petite question : disons que j'ai un jar dépendant avec la portée du temps d'exécution. Le maven va-t-il chercher le jar au moment de la compilation ?

0 votes

@gks Non, il ne l'exigera pas au moment de la compilation.

9voto

Peter Lawrey Points 229686

Vous avez besoin au moment de la compilation de dépendances dont vous pourriez avoir besoin au moment de l'exécution. Cependant, de nombreuses bibliothèques fonctionnent sans toutes leurs dépendances possibles. Par exemple, une bibliothèque qui peut utiliser quatre bibliothèques XML différentes, mais qui n'a besoin que d'une seule pour fonctionner.

De nombreuses bibliothèques ont besoin d'autres bibliothèques à leur tour. Ces bibliothèques ne sont pas nécessaires au moment de la compilation, mais elles le sont au moment de l'exécution, c'est-à-dire lorsque le code est réellement exécuté.

0 votes

Pourriez-vous nous donner des exemples de telles bibliothèques qui ne seront pas nécessaires à la compilation mais qui le seront à l'exécution ?

1 votes

@Cristiano toutes les bibliothèques JDBC sont comme ça. De même que les bibliothèques qui implémentent une API standard.

4voto

AlexR Points 60796

En général, vous avez raison et c'est probablement la situation idéale si les dépendances au moment de l'exécution et de la compilation sont identiques.

Je vais vous donner 2 exemples où 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 provenant de différentes bibliothèques tierces, vous n'avez besoin que de B et C à la compilation et vous avez également besoin de D à l'exécution. Les programmes utilisent souvent le chargement dynamique des classes. Dans ce cas, vous n'avez pas besoin des 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 peuvent changer l'implémentation du journal cible au moment de l'exécution. Vous n'avez besoin que de SSL4J lui-même au moment de la compilation.

Exemple contraire lorsque vous avez besoin de plus de dépendances au moment de la compilation qu'au moment de l'exécution. Imaginez 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 seulement des bibliothèques nécessaires à l'environnement actuel au moment de l'exécution.

J'espère que mes explications vous aideront.

0 votes

Pouvez-vous expliquer pourquoi le langage C est nécessaire au moment de la compilation dans votre exemple ? J'ai l'impression (d'après stackoverflow.com/a/7257518/6095334 ) que le fait que C soit nécessaire ou non au moment de la compilation dépend des méthodes et des champs (de B) auxquels A fait référence.

3voto

DaveFar Points 3360

Habituellement, le graphe des dépendances statiques est un sous-graphe du graphe dynamique, voir par exemple cet article de blog de l'auteur de NDepend .

Cela dit, il y a quelques exceptions, principalement les dépendances qui ajoutent le support du compilateur, qui devient invisible au moment de l'exécution. Par exemple, pour la génération de code via Lombok ou des contrôles supplémentaires comme via le Cadre de contrôle de type (enfichable) .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X