¿Existe alguna forma de excluir los artefactos heredados de un POM principal?

119

Los artefactos de las dependencias se pueden excluir declarando un <exclusions>elemento dentro de un <dependency>Pero en este caso es necesario excluir un artefacto heredado de un proyecto principal. A continuación, se muestra un extracto del POM en discusión:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>test</groupId>
  <artifactId>jruby</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
    </parent>

    <dependencies>      
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>ALL-DEPS</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</project>

baseartefacto, depende javax.mail:mail-1.4.jary ALL-DEPSdepende de otra versión de la misma biblioteca. Debido al hecho de que mail.jardesde ALL-DEPSexiste en el entorno de ejecución, aunque no se exporta, choca con el mail.jarque existe en el padre, que tiene el alcance compile.

Una solución podría ser eliminar mail.jar del POM principal, pero la mayoría de los proyectos que heredan la base la necesitan (como es una dependencia transtiva para log4j). Entonces, lo que me gustaría hacer es simplemente excluir la biblioteca principal del proyecto secundario , ya que se podría hacer si basefuera una dependencia y no el pom principal:

...
    <dependency>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
        <type>pom<type>
        <exclusions>
          <exclusion>
             <groupId>javax.mail</groupId>
             <artifactId>mail</artifactId>
          </exclusion>
        </exclusions>
    </dependency>
...
Miguel
fuente

Respuestas:

49

Algunas ideas:

  1. Tal vez simplemente no podría heredar del padre en ese caso (y declarar una dependencia basecon la exclusión). No es útil si tiene muchas cosas en el pom principal.

  2. Otra cosa para probar sería declarar el mailartefacto con la versión requerida por ALL-DEPSdebajo del dependencyManagementen el pom principal para forzar la convergencia (aunque no estoy seguro de que esto resuelva el problema de alcance).

<dependencyManagement>
  <dependencies>
    <dependency>    
      <groupId>javax.mail</groupId>
      <artifactId>mail</artifactId>
      <version>???</version><!-- put the "right" version here -->
    </dependency>
  </dependencies>
</dependencyManagement>
  1. O puede excluir la maildependencia de log4j si no está utilizando las funciones que dependen de él (y esto es lo que haría):
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.15</version>
  <scope>provided</scope>
  <exclusions>
    <exclusion>
      <groupId>javax.mail</groupId>
      <artifactId>mail</artifactId>
    </exclusion>
    <exclusion>
      <groupId>javax.jms</groupId>
      <artifactId>jms</artifactId>
    </exclusion>
    <exclusion>
      <groupId>com.sun.jdmk</groupId>
      <artifactId>jmxtools</artifactId>
    </exclusion>
    <exclusion>
      <groupId>com.sun.jmx</groupId>
      <artifactId>jmxri</artifactId>
    </exclusion>
  </exclusions>
</dependency>
  1. O puede volver a la versión 1.2.14 de log4j en lugar de la versión hereje 1.2.15 (¿por qué no marcaron las dependencias anteriores como opcionales ?).
Pascal Thivent
fuente
Gracias por su respuesta. Contiene una gran cantidad de información útil. Con respecto a 1) Como ha notado, no será óptimo porque el pom principal no solo contiene dependencias que de manera transitiva se resolverán si la base se marcó como una dependencia, sino también informes comunes, administración de fuentes y otras cosas reutilizadas entre todos los proyectos de la empresa. En relación con 2) lo probé, pero también especificando el alcance del artefacto como se proporcionó, y funcionó :). Al principio, pensé que, dado que la compilación tiene prioridad sobre la provista, no funcionará, pero afortunadamente estaba equivocado (el POM secundario anula la configuración del padre)
Miguel
29

Puede agrupar sus dependencias dentro de un proyecto diferente con un paquete pomcomo se describe en las Mejores prácticas de Sonatypes :

<project>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>base-dependencies</artifactId>
    <groupId>es.uniovi.innova</groupId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <dependencies>
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4</version>
        </dependency>
    </dependencies>
</project>

y haga referencia a ellos desde su parent-pom (observe la dependencia <type>pom</type>):

<project>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>base</artifactId>
    <groupId>es.uniovi.innova</groupId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <dependencies>
        <dependency>
            <artifactId>base-dependencies</artifactId>
            <groupId>es.uniovi.innova</groupId>
            <version>1.0.0</version>
            <type>pom</type>
        </dependency>
    </dependencies>
</project>

Su proyecto hijo hereda este pom principal como antes. Pero ahora, la dependencia de correo se puede excluir en el proyecto hijo dentro del dependencyManagementbloque:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>test</groupId>
    <artifactId>jruby</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <artifactId>base-dependencies</artifactId>
                <groupId>es.uniovi.innova</groupId>
                <version>1.0.0</version>
                <exclusions>
                    <exclusion>
                        <groupId>javax.mail</groupId>
                        <artifactId>mail</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
timomeinen
fuente
4
+1 para esto, aunque el pom hijo debería usar la sección <dependencies> en lugar de <dependencyManagement> ya que este último es para administrar las versiones de las dependencias desde dentro de un pom padre.
Matthew Wise
1
Esto también se conoce como lista de materiales, también conocida como lista de materiales :-)
Pim Hazebroek
¿Esto solo funciona con dependencias transitivas? Mi pom principal contiene log4j e impide que el logback de mi pom funcione correctamente.
Sridhar Sarnobat
10

No uses un pom principal

Esto puede sonar extremo, pero de la misma manera que el "infierno de la herencia" es una razón por la que algunas personas dan la espalda a la Programación Orientada a Objetos (o prefieren la composición a la herencia ), eliminan el <parent>bloque problemático y copian y pegan lo <dependencies>que necesiten (si su equipo le da esta libertad).

Se debe ignorar la suposición de que la división de poms en padres e hijos para "reutilizarlos" y "evitar la redundancia" debe ser ignorada y usted debe atender sus necesidades inmediatas primero (la cura es peor que la enfermedad). Además, la redundancia tiene sus ventajas, a saber, la independencia de los cambios externos (es decir, la estabilidad).

Esto es más fácil de lo que parece si genera el pom efectivo (eclipse lo proporciona pero puede generarlo desde la línea de comandos con mvn help:effective).

Ejemplo

Quiero usarlo logbackcomo mi enlace slf4j, pero mi pom principal incluye la log4jdependencia. No quiero ir y tener que empujar la dependencia de los otros niños a log4j en sus propios pom.xmlarchivos para que el mío no esté obstruido.

Sridhar Sarnobat
fuente
1
Documente esto (y el procedimiento que siguió) con mucho cuidado, ya que los futuros mantenedores deben rehacer esto si necesitan usar una versión actualizada del pom principal.
Thorbjørn Ravn Andersen
¿Seguramente te refieres a no usar dependencias en el pom principal? Parent pom sigue siendo muy útil para administrar versiones de dependencia y complementos comunes. Usarlo para inyectar dependencia puede ser contraproducente: lo usamos solo para las dependencias imprescindibles (como un conjunto de arrancadores de arranque de primavera esenciales para un padre para microservicios)
Amit Goldstein
No, no estoy diciendo que uses un pom principal ligero. Otros miembros de su equipo no le permitirán recortar el pom principal porque otras aplicaciones dependen de la basura en el pom principal que no desea.
Sridhar Sarnobat
8

Redefina la dependencia (en el pom hijo) con el scopesistema apuntando a un frasco vacío:

<dependency>
    <groupId>dependency.coming</groupId>
    <artifactId>from.parent</artifactId>
    <version>0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/empty.jar</systemPath>
</dependency>

El frasco puede contener solo un archivo vacío:

touch empty.txt
jar cvf empty.txt
Bax
fuente
Gracias por su respuesta, en Windows 10 tuve que correr notepad empty.classa continuación, jar cvf empty.jar empty.classpara generar un frasco vacío.
Rov
1
Parece una mala práctica
Piotr Żak
6

¿Ha intentado declarar explícitamente la versión de mail.jar que desea? La resolución de dependencia de Maven debería usar esto para la resolución de dependencia sobre todas las demás versiones.

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>test</groupId>
  <artifactId>jruby</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
    </parent>
    <dependencies>          
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>VERSION-#</version>
            <scope>provided</scope>
        </dependency> 
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>ALL-DEPS</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</project>
porterhouse91
fuente
1
Su enfoque, como la solución alternativa n. ° 2 de Pascal, también es válido; de hecho, también tuvo en cuenta que la dependencia de correo debe declararse como se proporciona. Gracias.
Miguel
El alcance proporcionado no funcionó para mí. Utilicé la prueba de alcance, verifique mi respuesta. stackoverflow.com/a/55970293/4587961
Yan Khonski
3

La mejor opción es hacer que las dependencias que no siempre desea heredar sean intransitivas.

Puede hacer esto marcándolos en el pom principal con el alcance proporcionado.

Si aún desea que el padre administre las versiones de estos departamentos, puede usar la <dependencyManagement>etiqueta para configurar las versiones que desee sin heredarlas explícitamente o pasar esa herencia a los hijos.

Ajax
fuente
1

Cuando llama a un paquete pero no desea algunas de sus dependencias, puede hacer algo como esto (en este caso, no quería que se agregara el antiguo log4j porque necesitaba usar el más nuevo):

<dependency>
  <groupId>package</groupId>
  <artifactId>package-pk</artifactId>
  <version>${package-pk.version}</version>

  <exclusions>
    <exclusion>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
    </exclusion>
    <exclusion>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
    </exclusion>
  </exclusions>
</dependency>

<!-- LOG4J -->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.5</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>2.5</version>
</dependency>

Esto funciona para mí ... pero soy bastante nuevo en java / maven, por lo que tal vez no sea óptimo.

bastienb1991
fuente
Bienvenido a Stack Overflow y no dejes que la gente grosera que tengo constantemente descarte mis preguntas lo desanime de publicar.
Sridhar Sarnobat
1

Realmente necesitaba hacer esta cosa sucia ... Así es como

Redefiní esas dependencias con alcance test. Scope providedno funcionó para mí.

Usamos el complemento Spring Boot para construir fat jar. Tenemos un módulo común que define bibliotecas comunes, por ejemplo Springfox swagger-2. Mi superservicio debe tener un padre en común (no quiere hacerlo, ¡pero las reglas de la compañía obligan!)

Entonces mi padre o los comunes tienen pom.

<dependencyManagement>

    <!- I do not need Springfox in one child but in others ->

    <dependencies>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-bean-validators</artifactId>
            <version>${swagger.version}</version>
        </dependency>

       <!- All services need them ->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>${apache.poi.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

Y mi pom de súper servicio .

<name>super-service</name>
<parent>
    <groupId>com.company</groupId>
    <artifactId>common</artifactId>
    <version>1</version>
</parent>

<dependencies>

    <!- I don't need them ->

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-bean-validators</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-core</artifactId>
        <version>2.8.0</version>
        <scope>test</scope>
    </dependency>

    <!- Required dependencies ->

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
     <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
    </dependency>
</dependencies>

Este es el tamaño del artefacto de grasa final.

82.3 MB (86,351,753 bytes) - redefined dependency with scope test
86.1 MB (90,335,466 bytes) - redefined dependency with scope provided
86.1 MB (90,335,489 bytes) - without exclusion

También vale la pena mencionar esta respuesta: quería hacerlo, pero soy vago ... https://stackoverflow.com/a/48103554/4587961

Yan Khonski
fuente
0

Podemos agregar el pom principal como una dependencia con el tipo pom y excluirlo. Porque de todos modos se descarga parent pom. Esto funcionó para mi

<dependency>
  <groupId>com.abc.boot</groupId>
  <artifactId>abc-boot-starter-parent</artifactId>
  <version>2.1.5.RELEASE</version>
  <type>pom</type>
  <exclusions>
    <exclusion>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
    </exclusion>
  </exclusions>   
</dependency>
Viki Jain
fuente