Quelle est la différence entre le moment de la compilation et les dépendances d'exécution en Java? Il est lié au chemin de classe, mais en quoi diffèrent-ils?
Réponses:
Dépendance au moment de la compilation : vous avez besoin de la dépendance dans votre CLASSPATH
pour compiler votre artefact. Ils sont produits parce que vous avez une sorte de "référence" à la dépendance codée en dur dans votre code, comme l'appel new
à une classe, l'extension ou l'implémentation de quelque chose (directement ou indirectement), ou un appel de méthode utilisant la reference.method()
notation directe .
La dépendance d' exécution : Vous avez besoin de la dépendance dans votre CLASSPATH
pour exécuter votre artefact. Ils sont produits parce que vous exécutez du code qui accède à la dépendance (soit de manière codée en dur, soit par réflexion ou autre).
Bien que la dépendance à la compilation implique généralement une dépendance à l'exécution, vous pouvez avoir une dépendance uniquement à la compilation. Ceci est basé sur le fait que Java lie uniquement les dépendances de classe lors du premier accès à cette classe, donc si vous n'accédez jamais à une classe particulière au moment de l'exécution car un chemin de code n'est jamais traversé, Java ignorera à la fois la classe et ses dépendances.
Exemple de ceci
Dans C.java (génère C.class):
package dependencies;
public class C { }
Dans A.java (génère A.class):
package dependencies;
public class A {
public static class B {
public String toString() {
C c = new C();
return c.toString();
}
}
public static void main(String[] args) {
if (args.length > 0) {
B b = new B();
System.out.println(b.toString());
}
}
}
Dans ce cas, A
a une dépendance au moment de la compilation sur C
through B
, mais il n'aura une dépendance à l'exécution sur C que si vous passez certains paramètres lors de l'exécution java dependencies.A
, car la machine virtuelle Java essaiera uniquement de résoudre B
la dépendance de la C
date d'exécution B b = new B()
. Cette fonctionnalité vous permet de fournir au moment de l'exécution uniquement les dépendances des classes que vous utilisez dans vos chemins de code et d'ignorer les dépendances du reste des classes dans l'artefact.
Un exemple simple est de regarder une API comme l'API de servlet. Pour faire compiler vos servlets, vous avez besoin du servlet-api.jar, mais au moment de l'exécution, le conteneur de servlet fournit une implémentation de servlet api, vous n'avez donc pas besoin d'ajouter servlet-api.jar à votre chemin de classe d'exécution.
Le compilateur a besoin du bon chemin de classe pour compiler les appels à une bibliothèque (dépendances au moment de la compilation)
La JVM a besoin du bon chemin de classe pour charger les classes dans la bibliothèque que vous appelez (dépendances d'exécution).
Ils peuvent être différents de plusieurs manières:
1) si votre classe C1 appelle la classe de bibliothèque L1 et que L1 appelle la classe de bibliothèque L2, alors C1 a une dépendance d'exécution sur L1 et L2, mais seulement une dépendance de compilation sur L1.
2) si votre classe C1 instancie dynamiquement une interface I1 à l'aide de Class.forName () ou d'un autre mécanisme, et que la classe d'implémentation de l'interface I1 est la classe L1, alors C1 a une dépendance d'exécution sur I1 et L1, mais seulement une dépendance de temps de compilation sur I1.
Autres dépendances "indirectes" qui sont les mêmes pour la compilation et l'exécution:
3) votre classe C1 étend la classe de bibliothèque L1, et L1 implémente l'interface I1 et étend la classe de bibliothèque L2: C1 a une dépendance à la compilation sur L1, L2 et I1.
4) votre classe C1 a une méthode foo(I1 i1)
et une méthode bar(L1 l1)
où I1 est une interface et L1 est une classe qui prend un paramètre qui est l'interface I1: C1 a une dépendance à la compilation sur I1 et L1.
Fondamentalement, pour faire quelque chose d'intéressant, votre classe doit s'interfacer avec d'autres classes et interfaces dans le chemin de classe. Le graphique de classe / interface formé par cet ensemble d' interfaces de bibliothèque donne la chaîne de dépendances à la compilation. Les implémentations de bibliothèque génèrent la chaîne de dépendances d'exécution. Notez que la chaîne de dépendances d'exécution dépend de l'exécution ou est lente: si l'implémentation de L1 dépend parfois de l'instanciation d'un objet de la classe L2, et que cette classe n'est instanciée que dans un scénario particulier, alors il n'y a pas de dépendance sauf dans ce scénario.
Java ne lie en fait rien au moment de la compilation. Il vérifie uniquement la syntaxe à l'aide des classes correspondantes qu'il trouve dans le CLASSPATH. Ce n'est qu'au moment de l'exécution que tout est assemblé et exécuté sur la base du CLASSPATH à ce moment-là.
Les dépendances de compilation ne sont que les dépendances (autres classes) que vous utilisez directement dans la classe que vous compilez. Les dépendances d'exécution couvrent à la fois les dépendances directes et indirectes de la classe que vous exécutez. Ainsi, les dépendances d'exécution incluent les dépendances des dépendances et toutes les dépendances de réflexion telles que les noms de classe que vous avez dans a String
, mais qui sont utilisés dans Class#forName()
.
A
, B.jar avec B extends A
et C.jar avec C extends B
alors C.jar dépend du temps de compilation sur A.jar même si la dépendance de C sur A est indirecte.
Pour Java, la dépendance au moment de la compilation est la dépendance de votre code source. Par exemple, si la classe A appelle une méthode de la classe B, alors A dépend de B au moment de la compilation car A doit connaître B (type de B) pour être compilé. L'astuce ici devrait être la suivante: le code compilé n'est pas encore un code complet et exécutable. Il comprend des adresses remplaçables (symboles, métadonnées) pour les sources qui ne sont pas encore compilées ou qui existent dans des jars externes. Lors de la liaison, ces adresses doivent être remplacées par des adresses réelles dans la mémoire. Pour le faire correctement, des symboles / adresses corrects doivent être créés. Et cela peut être fait avec le type de classe (B). Je crois que c'est la principale dépendance au moment de la compilation.
La dépendance d'exécution est davantage liée au flux de contrôle réel. Il explore les adresses mémoire réelles. C'est une dépendance que vous avez lorsque votre programme est en cours d'exécution. Vous avez besoin de détails de classe B ici, comme des implémentations, pas seulement des informations de type. Si la classe n'existe pas, vous obtiendrez RuntimeException et JVM se fermera.
Les deux dépendances, généralement et ne devraient pas, circuler dans la même direction. C'est une question de conception OO.
En C ++, la compilation est un peu différente (pas juste à temps) mais elle a aussi un éditeur de liens. Donc, le processus pourrait être considéré comme similaire à Java, je suppose.