Entiendo la diferencia entre tiempo de ejecución y tiempo de compilación y cómo diferenciar entre los dos, pero no veo la necesidad de hacer una distinción entre dependencias de tiempo de compilación y tiempo de ejecución .
Con lo que me estoy asfixiando es esto: ¿cómo puede un programa no depender de algo en tiempo de ejecución de lo que dependía durante la compilación? Si mi aplicación Java usa log4j, entonces necesita el archivo log4j.jar para compilar (mi código se integra e invoca métodos de miembros desde dentro de log4j) así como el tiempo de ejecución (mi código no tiene absolutamente ningún control sobre lo que sucede una vez que el código dentro de log4j .jar se ejecuta).
Estoy leyendo sobre herramientas de resolución de dependencias como Ivy y Maven, y estas herramientas claramente hacen la distinción entre estos dos tipos de dependencias. Simplemente no entiendo la necesidad de hacerlo.
¿Alguien puede dar una explicación simple del tipo "King's English", preferiblemente con un ejemplo real que incluso un pobre idiota como yo podría entender?
fuente
Respuestas:
Generalmente se requiere una dependencia en tiempo de compilación en tiempo de ejecución. En maven,
compile
se agregará una dependencia de ámbito a la ruta de clase en tiempo de ejecución (por ejemplo, en guerras, se copiarán en WEB-INF / lib).Sin embargo, no es estrictamente necesario; por ejemplo, podemos compilar con una determinada API, convirtiéndola en una dependencia en tiempo de compilación, pero luego, en tiempo de ejecución, incluir una implementación que también incluya la API.
Puede haber casos marginales en los que el proyecto requiera cierta dependencia para compilarse, pero luego el código correspondiente no es realmente necesario, pero estos serán raros.
Por otro lado, es muy común incluir dependencias en tiempo de ejecución que no son necesarias en tiempo de compilación. Por ejemplo, si está escribiendo una aplicación Java EE 6, compila con la API Java EE 6, pero en tiempo de ejecución, se puede utilizar cualquier contenedor Java EE; es este contenedor el que proporciona la implementación.
Las dependencias en tiempo de compilación se pueden evitar mediante la reflexión. Por ejemplo, un controlador JDBC se puede cargar con a
Class.forName
y la clase real cargada se puede configurar a través de un archivo de configuración.fuente
provided
alcance agrega una dependencia de tiempo de compilación sin agregar una dependencia de tiempo de ejecución con la expectativa de que la dependencia se proporcionará en tiempo de ejecución por otros medios (por ejemplo, una biblioteca compartida en el contenedor).runtime
por otro lado, agrega una dependencia en tiempo de ejecución sin convertirla en una dependencia en tiempo de compilación.Cada dependencia de Maven tiene un alcance que define en qué ruta de clase está disponible esa dependencia.
Cuando crea un JAR para un proyecto, las dependencias no se empaquetan con el artefacto generado; se utilizan solo para la compilación. (Sin embargo, aún puede hacer que maven incluya las dependencias en el jar construido, consulte: Incluir dependencias en un jar con Maven )
Cuando usa Maven para crear un archivo WAR o EAR, puede configurar Maven para agrupar dependencias con el artefacto generado, y también puede configurarlo para excluir ciertas dependencias del archivo WAR utilizando el alcance proporcionado.
El alcance más común - Alcance de compilación - indica que la dependencia está disponible para su proyecto en la ruta de clases de compilación, las rutas de clases de ejecución y compilación de pruebas unitarias y la ruta de clases en tiempo de ejecución eventual cuando ejecuta su aplicación. En una aplicación web Java EE, esto significa que la dependencia se copia en su aplicación implementada. Sin embargo, en un archivo .jar, las dependencias no se incluirán en el ámbito de compilación.
Runtime Scope indica que la dependencia está disponible para su proyecto en la ejecución de la prueba unitaria y en las rutas de clases de ejecución en tiempo de ejecución, pero a diferencia del alcance de compilación, no está disponible cuando compila su aplicación o sus pruebas unitarias. Una dependencia de tiempo de ejecución se copia en su aplicación implementada, pero no está disponible durante la compilación. Esto es bueno para asegurarse de no depender por error de una biblioteca específica.
Finalmente, Provided Scope indica que el contenedor en el que se ejecuta su aplicación proporciona la dependencia en su nombre. En una aplicación Java EE, esto significa que la dependencia ya está en la ruta de clase del servidor de aplicaciones o del contenedor Servlet y no se copia en la aplicación implementada. También significa que necesita esta dependencia para compilar su proyecto.
fuente
Necesita en tiempo de compilación dependencias que pueda necesitar en tiempo de ejecución. Sin embargo, muchas bibliotecas se ejecutan sin todas sus posibles dependencias. es decir, una biblioteca que puede usar cuatro bibliotecas XML diferentes, pero solo necesita una para funcionar.
Muchas bibliotecas, a su vez, necesitan otras bibliotecas. Estas bibliotecas no son necesarias en tiempo de compilación, pero sí en tiempo de ejecución. es decir, cuando el código se ejecuta realmente.
fuente
Generalmente tiene razón y probablemente es la situación ideal si las dependencias de tiempo de ejecución y tiempo de compilación son idénticas.
Le daré 2 ejemplos cuando esta regla es incorrecta.
Si la clase A depende de la clase B, que depende de la clase C, que depende de la clase D, donde A es su clase y B, C y D son clases de diferentes bibliotecas de terceros, solo necesita B y C en tiempo de compilación y también necesita D en tiempo de ejecución. A menudo, los programas utilizan la carga de clases dinámica. En este caso, no necesita clases cargadas dinámicamente por la biblioteca que está utilizando en tiempo de compilación. Además, a menudo, la biblioteca elige qué implementación usar en tiempo de ejecución. Por ejemplo, SLF4J o Commons Logging pueden cambiar la implementación del registro de destino en tiempo de ejecución. Solo necesita SSL4J en el momento de la compilación.
Ejemplo opuesto cuando necesita más dependencias en tiempo de compilación que en tiempo de ejecución. Piense que está desarrollando una aplicación que debe funcionar en diferentes entornos o sistemas operativos. Necesita todas las bibliotecas específicas de la plataforma en tiempo de compilación y solo las bibliotecas necesarias para el entorno actual en tiempo de ejecución.
Espero que mis explicaciones ayuden.
fuente
Por lo general, el gráfico de dependencias estáticas es un subgráfico del dinámico; consulte, por ejemplo, esta entrada de blog del autor de NDepend .
Dicho esto, hay algunas excepciones, principalmente dependencias que agregan soporte para compiladores, que se vuelve invisible en tiempo de ejecución. Por ejemplo, para la generación de código a través de Lombok o comprobaciones adicionales a través del Checker Framework (tipo conectable) .
fuente
Me encontré con un problema que responde a su pregunta.
servlet-api.jar
es una dependencia transitoria en mi proyecto web y se necesita tanto en tiempo de compilación como en tiempo de ejecución. Peroservlet-api.jar
también está incluido en mi biblioteca Tomcat.La solución aquí es hacer que
servlet-api.jar
en maven esté disponible solo en el momento de la compilación y no empaquetado en mi archivo war para que no entre en conflicto con elservlet-api.jar
contenido en mi biblioteca Tomcat.Espero que esto explique el tiempo de compilación y la dependencia del tiempo de ejecución.
fuente
compile
yprovided
alcances y no entrecompile
yruntime
.Compile scope
se necesita en tiempo de compilación y está empaquetado en su aplicación.Provided scope
solo se necesita en el momento de la compilación, pero no está empaquetado en su aplicación porque lo proporciona otro medio, por ejemplo, ya está en el servidor Tomcat.compile
yruntime
alcances Maven . Elprovided
alcance es la forma en que maven maneja el caso en el que una dependencia en tiempo de compilación no debe incluirse en el paquete de tiempo de ejecución.Los conceptos generales de tiempo de compilación y tiempo de ejecución y las dependencias específicas
compile
y deruntime
alcance de Maven son dos cosas muy diferentes. No puede compararlos directamente ya que estos no tienen el mismo marco: los conceptos generales de compilación y tiempo de ejecución son amplios, mientras que los conceptos de mavencompile
yruntime
alcance tratan específicamente sobre la disponibilidad / visibilidad de las dependencias según el tiempo: compilación o ejecución.No olvides que Maven es sobre todo un
javac
/java
wrapper y que en Java tienes una ruta de clase de tiempo de compilación que especificasjavac -cp ...
y una ruta de clase de tiempo de ejecución que especificasjava -cp ...
.No sería incorrecto considerar el
compile
alcance de Maven como una forma de agregar una dependencia tanto en la compilación de Java como en la classppath en tiempo de ejecución (javac
yjava
) mientras que elruntime
alcance de Maven puede verse como una forma de agregar una dependencia solo en el tiempo de ejecución de Java classppath (javac
).Lo que usted describe no tiene ninguna relación
runtime
nicompile
alcance.Se parece más al
provided
alcance que especifica para que una dependencia dependa de eso en el tiempo de compilación pero no en el tiempo de ejecución.Lo usa porque necesita la dependencia para compilar, pero no desea incluirlo en el componente empaquetado (JAR, WAR o cualquier otro) porque la dependencia ya la proporciona el entorno: se puede incluir en el servidor o en cualquier Se inicia la ruta de la ruta de clases especificada cuando se inicia la aplicación Java.
En este caso sí. Pero suponga que necesita escribir un código portátil que se base en slf4j como fachada frente a log4j para poder cambiar a otra implementación de registro más adelante (log4J 2, logback o cualquier otro).
En este caso, en su pom, debe especificar slf4j como una
compile
dependencia (es el valor predeterminado) pero especificará la dependencia log4j como unaruntime
dependencia:De esta manera, las clases log4j no podrían ser referenciadas en el código compilado pero aún podrá referirse a clases slf4j.
Si especificó las dos dependencias con el
compile
tiempo, nada le impedirá hacer referencia a las clases log4j en el código compilado y podría crear un acoplamiento no deseado con la implementación de registro:Un uso común del
runtime
alcance es la declaración de dependencia de JDBC. Para escribir código portátil, no desea que el código del cliente pueda referirse a clases de la dependencia DBMS específica (por ejemplo: dependencia PostgreSQL JDBC) pero desea que de todos modos lo incluya en su aplicación, ya que en tiempo de ejecución las clases son necesarias para hacer la API de JDBC funciona con este DBMS.fuente
En el momento de la compilación, habilita los contratos / api que se espera de sus dependencias. (por ejemplo: aquí simplemente firma un contrato con el proveedor de Internet de banda ancha) En el tiempo de ejecución, en realidad está utilizando las dependencias. (por ejemplo: aquí realmente está utilizando Internet de banda ancha)
fuente
Para responder a la pregunta "¿cómo puede un programa no depender de algo en tiempo de ejecución de lo que dependía durante la compilación?", Veamos el ejemplo de un procesador de anotaciones.
Suponga que ha escrito su propio procesador de anotaciones, y suponga que tiene una dependencia en tiempo de compilación
com.google.auto.service:auto-service
para poder usar@AutoService
. Esta dependencia solo es necesaria para compilar el procesador de anotaciones, pero no es necesaria en tiempo de ejecución: todos los demás proyectos que dependen de su procesador de anotaciones para procesar anotaciones no requieren la dependencia decom.google.auto.service:auto-service
en tiempo de ejecución (ni en tiempo de compilación ni en ningún otro momento) .Esto no es muy común, pero sucede.
fuente
El
runtime
alcance está ahí para evitar que los programadores agreguen dependencias directas a las bibliotecas de implementación en el código en lugar de usar abstracciones o fachadas.En otras palabras, obliga a utilizar interfaces.
Ejemplos concretos:
1) Su equipo está usando SLF4J sobre Log4j. Quiere que sus programadores usen la API de SLF4J, no la de Log4j. SLF4J debe utilizar Log4j solo internamente. Solución:
2) Su aplicación está accediendo a MySQL usando JDBC. Quiere que sus programadores codifiquen contra la abstracción estándar de JDBC, no directamente contra la implementación del controlador MySQL.
mysql-connector-java
(controlador MySQL JDBC) como una dependencia de tiempo de ejecución.Las dependencias en tiempo de ejecución se ocultan durante la compilación (arrojando errores en tiempo de compilación si su código tiene una dependencia "directa" de ellas) pero se incluyen durante el tiempo de ejecución y al crear artefactos desplegables (archivos WAR, archivos jar SHADED, etc.).
fuente