tutorial tienda sistema programa con biblioteca java build-process runtime compile-time

tienda - sistema de biblioteca en java y mysql



¿Puede un programa depender de una biblioteca durante la compilación pero no de tiempo de ejecución? (8)

Acabo de toparme con un problema que responde a tu pregunta. servlet-api.jar es una dependencia transitoria en mi proyecto web y es necesaria tanto en tiempo de compilación como en tiempo de ejecución. Pero servlet-api.jar también se incluye en mi biblioteca Tomcat.

La solución aquí es hacer que servlet-api.jar en maven esté disponible solo en tiempo de compilación y no empaquetado en mi archivo war para que no entre en conflicto con el servlet-api.jar contenido en mi biblioteca Tomcat.

Espero que esto explique el tiempo de compilación y la dependencia de tiempo de ejecución.

Entiendo la diferencia entre tiempo de ejecución y tiempo de compilación y cómo diferenciar entre los dos, pero simplemente no veo la necesidad de hacer una distinción entre las dependencias en tiempo de compilación y en tiempo de ejecución.

A lo que me estoy ahogando es a 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, necesita el archivo log4j.jar para compilar (mi código integrando e invocando métodos miembro desde adentro log4j) así como el tiempo de ejecución (mi código no tiene absolutamente ningún control sobre lo que sucede una vez código dentro de log4j .jar se ejecutó).

Estoy leyendo herramientas de resolución de dependencia como Ivy y Maven, y estas herramientas hacen claramente la distinción entre estos dos tipos de dependencias. Simplemente no entiendo la necesidad de eso.

¿Alguien puede dar una explicación simple, tipo "King''s English", preferiblemente con un ejemplo real que incluso una pobre savia como yo podría entender?


Cada dependencia de Maven tiene un alcance que define en qué classpath esa dependencia está disponible.

Cuando crea un JAR para un proyecto, las dependencias no se incluyen con el artefacto generado; se usan solo para compilación. (Sin embargo, aún puede hacer que maven incluya las dependencias en el jar construido, ver: Incluir dependencias en un jar con Maven )

Cuando utiliza 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 provisto.

El ámbito 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 compilación y ejecución de unidades, y la ruta de clases de tiempo de ejecución final cuando ejecuta su aplicación. En una aplicación web Java EE, esto significa que la dependencia se copia en la aplicación desplegada. Sin embargo, en un archivo .jar, las dependencias no se incluirán con el ámbito de compilación.

Runtime Scope indica que la dependencia está disponible para su proyecto en la ejecución de prueba unitaria y classpaths 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 la 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. (Ver por ejemplo: http://www.tugay.biz/2016/12/apache-commons-logging-log4j-maven.html )

Finalmente, Alcance Proporcionado 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 el classpath del servidor de aplicaciones o del servidor de aplicaciones y no se copia en la aplicación desplegada. También significa que necesita esta dependencia para compilar su proyecto.


En tiempo de compilación, habilita los contratos / api que se esperan de sus dependencias. (por ejemplo: aquí solo firmas un contrato con un proveedor de Internet de banda ancha) En tiempo de ejecución, en realidad estás usando las dependencias. (Por ejemplo: aquí en realidad estás usando Internet de banda ancha)


Necesita en tiempo de compilación las 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 trabajar.

Muchas bibliotecas necesitan otras bibliotecas a su vez. Estas bibliotecas no son necesarias en tiempo de compilación, pero son necesarias en tiempo de ejecución. es decir, cuando el código realmente se ejecuta.


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 anotación.

Supongamos que ha escrito su propio procesador de anotaciones y suponga que tiene una dependencia en tiempo de compilación en com.google.auto.service:auto-service para que pueda usar @AutoService . Esta dependencia solo es necesaria para compilar el procesador de anotaciones, pero no es necesario en tiempo de ejecución: todos los demás proyectos que dependen de su procesador de anotación para procesar anotaciones no requieren la dependencia de com.google.auto.service:auto-service en el tiempo de ejecución ( ni en tiempo de compilación ni en ningún otro momento).

Esto no es muy común, pero sucede.


Por lo general, el gráfico de dependencias estáticas es un sub-gráfico de la dinámica, ver, por ejemplo, esta entrada de blog del autor de NDepend .

Dicho esto, hay algunas excepciones, principalmente dependencias que agregan soporte de compilador, que se vuelve invisible en el tiempo de ejecución. Por ejemplo, para la generación de código a través de Lombok o comprobaciones adicionales como a través del marco Checker (tipo conectable) .


Por lo general, usted está en lo cierto y probasiblemente 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 el momento de la compilación y también necesita D en tiempo de ejecución A menudo los programas usan carga dinámica de clase. 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 log de destino en tiempo de ejecución. Solo necesita SSL4J en tiempo de compilación.

Ejemplo opuesto cuando necesita más dependencias en tiempo de compilación que en tiempo de ejecución. Piensa que estás desarrollando una aplicación que debe funcionar en diferentes entornos o sistemas operativos. Necesita todas las bibliotecas específicas de 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.


En general, se requiere una dependencia de tiempo de compilación en tiempo de ejecución. En maven, se agregará una dependencia de ámbito de compile a classpath en tiempo de ejecución (por ejemplo, en wars se copiarán en WEB-INF / lib).

Sin embargo, no es estrictamente obligatorio; por ejemplo, podemos compilar contra una cierta API, convirtiéndola en una dependencia en tiempo de compilación, pero luego en el tiempo de ejecución incluimos una implementación que también incluye la API.

Puede haber casos adicionales en los que el proyecto requiera una cierta dependencia para compilarse, pero luego el código correspondiente no es realmente necesario, pero estos serán raros.

Por otro lado, incluir dependencias de tiempo de ejecución que no son necesarias en tiempo de compilación es muy común. Por ejemplo, si está escribiendo una aplicación Java EE 6, compila en contra de la API de Java EE 6, pero en tiempo de ejecución, se puede usar cualquier contenedor Java EE; es este contenedor el que proporciona la implementación.

Las dependencias en tiempo de compilación se pueden evitar utilizando el reflejo. Por ejemplo, un controlador JDBC puede cargarse con un Class.forName y la clase real cargada puede configurarse a través de un archivo de configuración.