Conocimiento básico imprescindible de la diferencia entre gradle y maven

Comprensión profunda de la diferencia entre gradle y maven

Tanto gradle como maven se pueden usar para construir programas java, e incluso en algunos casos, los dos se pueden convertir entre sí, entonces, ¿cuáles son los puntos comunes y diferentes de los dos? ¿Cómo elegimos qué tecnología utilizar en el proyecto? Vamos a ver.

Comparación de gradle y maven

Aunque tanto gradle como maven se pueden usar como herramientas de compilación para programas java. Pero aún existen grandes diferencias entre los dos. Podemos analizar desde los siguientes aspectos.

Escalabilidad

Google eligió gradle como la herramienta de compilación de Android sin ningún motivo. Una de las razones más importantes es que gradle es lo suficientemente flexible. Por un lado, debido a que gradle usa groovy o kotlin como lenguaje de scripting, esto mejora enormemente la flexibilidad del script, pero la razón esencial es que la infraestructura de Gradle puede soportar esta flexibilidad.

Puede usar gradle para crear programas nativos C / C ++ e incluso extenderlo a cualquier idioma.

En términos relativos, maven es menos flexible y más problemático de personalizar, pero los proyectos de maven son más fáciles de entender y de usar.

Entonces, si su proyecto no tiene demasiados requisitos de compilación personalizados, de todos modos se recomienda usar maven, pero si tiene requisitos de compilación personalizados, entonces debería invertir en gradle.

Comparación de rendimiento

Aunque el rendimiento de la máquina de todos es relativamente sólido ahora, parece que la ventaja de rendimiento no es tan urgente cuando se realiza la construcción de un proyecto, pero para proyectos grandes, una construcción puede llevar mucho tiempo, especialmente para la construcción automatizada y CI. En términos de medio ambiente, por supuesto , Espero que esta compilación sea lo más rápida posible.

Tanto Gradle como Maven admiten la construcción de proyectos paralelos y la resolución de dependencias. Pero tres características de gradle hacen que gradle se ejecute un poco más rápido que maven:

  • Construcción incremental

Para mejorar la eficiencia de la construcción, gradle propone el concepto de construcción incremental. Para lograr la construcción incremental, gradle divide cada tarea en tres partes, a saber, entrada de entrada, tarea en sí y salida de salida. La siguiente figura es una tarea típica compilada de Java.

Conocimiento básico imprescindible de la diferencia entre gradle y maven

Tome la imagen de arriba como ejemplo, la entrada es la versión de jdk de destino, el código fuente, etc., y la salida es el archivo de clase compilado.

El principio de la construcción incremental es monitorear los cambios de entrada, y solo cuando la entrada se envía para cambiar, la tarea se volverá a ejecutar; de lo contrario, gradle piensa que el resultado de la ejecución anterior se puede reutilizar.

Entonces, al escribir una tarea de Gradle, debe especificar la entrada y salida de la tarea.

Y debe tenerse en cuenta que solo aquellos que cambian el resultado de salida se pueden llamar entrada. Si define variables que no están relacionadas en absoluto con el resultado inicial como entrada, los cambios en estas variables harán que Gradle vuelva a ejecutar la tarea, lo que resultará en pérdida de rendimiento innecesaria.

También preste atención a las tareas con resultados de ejecución inciertos, por ejemplo, la misma entrada puede obtener resultados de salida diferentes, entonces tales tareas no podrán configurarse como tareas de construcción incrementales.

  • Crear caché

Gradle puede reutilizar la salida de la misma entrada como caché. Es posible que tenga preguntas. ¿No significan lo mismo este caché y la compilación incremental?

Sí, en la misma máquina, pero la caché se puede compartir entre máquinas. Si está en un servicio de CI, crear caché será muy útil. Porque la compilación del desarrollador puede extraer directamente los resultados de la compilación del servidor CI, lo cual es muy conveniente.

  • Demonio Gradle

Gradle iniciará un demonio para interactuar con cada tarea de compilación. La ventaja es que no necesita inicializar los componentes y servicios necesarios para cada compilación.

Al mismo tiempo, debido a que el daemon es un proceso que se ejecuta todo el tiempo, además de evitar la sobrecarga de cada inicio de JVM, también puede almacenar en caché la estructura del proyecto, los archivos, las tareas y otra información para mejorar la velocidad de ejecución.

Podemos ejecutar gradle -status para ver el proceso de los demonios en ejecución.

Desde Gradle 3.0, los demonios están habilitados de forma predeterminada. Puede usar org.gradle.daemon = false para deshabilitar demonios.

Podemos sentir intuitivamente la comparación de rendimiento de gradle y maven a través de las siguientes figuras:

  • Comparación del uso de gradle y maven para construir Apache Commons Lang 3:

Conocimiento básico imprescindible de la diferencia entre gradle y maven

  • Comparación del uso de gradle y maven para construir proyectos pequeños (10 módulos, 50 archivos fuente y 50 archivos de prueba para cada módulo):

Conocimiento básico imprescindible de la diferencia entre gradle y maven

  • Comparación del uso de gradle y maven para construir proyectos grandes (500 módulos, 100 archivos fuente y 100 archivos de prueba para cada módulo):

Conocimiento básico imprescindible de la diferencia entre gradle y maven

Puede ver que la mejora del rendimiento de gradle es muy obvia.

La diferencia de dependencia

Tanto gralde como maven pueden almacenar en caché archivos dependientes localmente y ambos admiten la descarga paralela de archivos dependientes.

En maven, el número de versión solo puede sobrescribir una dependencia. Gradle es más flexible. Puedes personalizar las dependencias y las reglas de reemplazo. A través de estas reglas de reemplazo, gradle puede crear proyectos muy complejos.

Migrar de maven a gradle

Debido a que maven apareció antes, básicamente todos los proyectos de Java son compatibles con Maven, pero no todos los proyectos son compatibles con Gradle. Si tiene una idea de migrar un proyecto de maven a gradle, echemos un vistazo.

De acuerdo con nuestra introducción anterior, puede encontrar que gradle y maven son esencialmente diferentes. Gradle organiza tareas a través de gráficos DAG de tareas, mientras que maven ejecuta tareas a través de objetivos adjuntos a fases.

Aunque la construcción de los dos es muy diferente, gracias a las diversas convenciones y reglas que cumplen gradle y maven, no es tan difícil migrar de maven a gradle.

Para migrar de maven a gradle, primero debe comprender el ciclo de vida de compilación de maven. El ciclo de vida de maven incluye las fases de limpieza, compilación, prueba, empaquetado, verificación, instalación e implementación.

Necesitamos convertir la fase del ciclo de vida de maven en tareas del ciclo de vida de Gradle. Aquí debe utilizar el complemento base de gradle, el complemento de Java y el complemento de publicación de Maven.

Primero observe cómo introducir estos tres complementos:

plugins {
    id 'base'
    id 'java'
    id 'maven-publish'
}

La limpieza se convertirá en una tarea limpia, la compilación se convertirá en una tarea de clases, la prueba se convertirá en una tarea de prueba, el paquete se convertirá en una tarea de ensamblaje, la verificación se convertirá en una tarea de verificación, la instalación se convertirá en el complemento de publicación de Maven PublishToMaven Tarea local , la implementación se convertirá en una tarea de publicación en Maven Publish Plugin.

Con la correspondencia entre estas tareas, podemos intentar convertir de maven a gradle.

Conversión automática

Además de usar el comando gradle init para crear un estante gradle, también podemos usar este comando para convertir un proyecto maven en un proyecto gradle. El comando gradle init leerá el archivo pom y lo convertirá en un proyecto gradle.

Dependencia de conversión

Tanto las dependencias de gradle como las de maven incluyen ID de grupo, ID de artefacto y número de versión. Los dos son esencialmente iguales, pero la forma es diferente. Veamos un ejemplo de conversión:

<dependencies>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
</dependencies>

Lo anterior es un ejemplo de maven, veamos cómo escribir el ejemplo de Gradle:

dependencies {
    implementation 'log4j:log4j:1.2.12'  
}

Puede ver que gradle es mucho más fácil de escribir que maven.

Tenga en cuenta que la implementación aquí está realmente implementada por Java Plugin.

A veces usamos la opción de alcance en las dependencias de maven para indicar el alcance de la dependencia. Veamos cómo se pueden convertir estos alcances:

  • compilar:

Hay dos configuraciones para reemplazar compilar en gradle, podemos usar implementación o api.

El primero se puede usar en cualquier gradle que use el complemento de Java, mientras que la api solo se puede usar en proyectos que usan el complemento de biblioteca de Java.

Por supuesto, hay una diferencia entre los dos. Si está creando aplicaciones o aplicaciones web, se recomienda la implementación. Si está creando bibliotecas Java, se recomienda api.

  • tiempo de ejecución:

Se puede reemplazar con runtimeOnly.

  • prueba:

Hay dos tipos de pruebas en gradle: una es necesaria al compilar el proyecto de prueba, luego se puede usar testImplementation y la otra es necesaria cuando se ejecuta el proyecto de prueba, luego se puede usar testRuntimeOnly.

  • previsto:

Puede ser reemplazado por compileOnly.

  • importar:

En maven, la importación se usa a menudo en dependencyManagement, generalmente se usa para importar dependencias de un archivo pom, a fin de garantizar la consistencia de la versión del proyecto dependiente en el proyecto.

En gradle, puede usar platform () o enforcePlatform () para importar archivos pom:

dependencies {
    implementation platform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE') 

    implementation 'com.google.code.gson:gson' 
    implementation 'dom4j:dom4j'
}

Por ejemplo, en el ejemplo anterior, importamos spring-boot-dependencies. Debido a que el número de versión de la dependencia ya está definido en este pom, no necesitamos especificar el número de versión cuando introducimos gson más adelante.

La diferencia entre plataforma y enforcePlatform es que enforcePlatform sobrescribirá el número de versión importada del pom:

dependencies {
    // import a BOM. The versions used in this file will override any other version found in the graph
    implementation enforcedPlatform('org.springframework.boot:spring-boot-dependencies:1.5.8.RELEASE')

    // define dependencies without versions
    implementation 'com.google.code.gson:gson'
    implementation 'dom4j:dom4j'

    // this version will be overridden by the one found in the BOM
    implementation 'org.codehaus.groovy:groovy:1.8.6'
}

Convertir repositorios

Gradle puede ser compatible con el uso del repositorio maven o lvy. Gradle no tiene una dirección de almacén predeterminada, por lo que debe especificar una manualmente.

Puede usar el repositorio de maven en gradle:

repositories {
    mavenCentral()
}

También podemos especificar directamente la dirección del almacén de maven:

repositories {
    maven {
        url "http://repo.mycompany.com/maven2"
    }
}

Si desea utilizar el almacén local de maven, puede utilizarlo así:

repositories {
    mavenLocal()
}

Pero mavenLocal no se recomienda, ¿por qué?

mavenLocal es solo un caché local de maven y su contenido no está completo. Por ejemplo, un módulo de repositorio maven local solo puede contener archivos jar, pero no archivos fuente o javadoc. Entonces no podremos ver el código fuente de este módulo en gradle, porque gradle primero buscará este módulo en la ruta local de maven.

Y no se puede confiar en el repositorio local, porque el contenido interno se puede modificar fácilmente sin ningún mecanismo de verificación.

Versión dependiente del control

Si hay dos dependencias de diferentes versiones para el mismo módulo en el mismo proyecto, de forma predeterminada, Gradle seleccionará el paquete de dependencia con la versión más alta después de analizar el DAG.

Pero esto no es necesariamente correcto, por lo que necesitamos personalizar las funciones que dependen de la versión.

El primero es el uso de platform () y enforcementPlatform () mencionados anteriormente para importar archivos BOM (el tipo de empaquetado es POM).

Si nuestro proyecto depende de un módulo determinado, y este módulo depende de otro módulo, lo llamamos dependencia transitiva. En este caso, si queremos controlar la versión de la dependencia transitiva, por ejemplo, actualizar la versión de la dependencia transitiva a una nueva versión, entonces podemos usar restricciones de dependencia:

dependencies {
    implementation 'org.apache.httpcomponents:httpclient'
    constraints {
        implementation('org.apache.httpcomponents:httpclient:4.5.3') {
            because 'previous versions have a bug impacting this application'
        }
        implementation('commons-codec:commons-codec:1.11') {
            because 'version 1.9 pulled from httpclient has bugs affecting this application'
        }
    }
}

Tenga en cuenta que las restricciones de dependencia solo son válidas para las dependencias transitivas. Si el códec común en el ejemplo anterior no es transitivo, no tendrá ningún efecto.

Al mismo tiempo, las restricciones de dependencia necesitan el soporte de los metadatos del módulo de Gradle, lo que significa que esta función solo es compatible si su módulo se lanza en gradle, y no es compatible si se lanza en maven o ivy.

Lo anterior es la actualización de la versión de las dependencias transitivas. Lo mismo es una dependencia transitiva. Si este proyecto también necesita usar este módulo dependiente transitivamente, pero necesita usar una versión más baja (porque el gradle predeterminado usará la última versión), debe usar la versión degradada.

dependencies {
    implementation 'org.apache.httpcomponents:httpclient:4.5.4'
    implementation('commons-codec:commons-codec') {
        version {
            strictly '1.9'
        }
    }
}

Podemos especificar una versión específica en la implementación.

Estricto significa forzar la coincidencia de un número de versión específico. Además de estrictamente, también es obligatorio, lo que significa que el número de versión requerido es mayor o igual al número de versión dado. preferir, si no se especifica ningún otro número de versión, use prefer. Rechazar, negarse a usar esta versión.

Además, también puede utilizar el complemento de plataforma Java para especificar una plataforma específica para limitar el número de versión.

Finalmente, mire cómo excluir una dependencia:

dependencies {
    implementation('commons-beanutils:commons-beanutils:1.9.4') {
        exclude group: 'commons-collections', module: 'commons-collections'
    }
}

Proyecto de varios módulos

Los proyectos de varios módulos se pueden crear en maven:

<modules>
    <module>simple-weather</module>
    <module>simple-webapp</module>
</modules>

Podemos hacer lo mismo en la configuración de Gradle.

rootProject.name = 'simple-multi-module'  

include 'simple-weather', 'simple-webapp'

perfil y atributos

El perfil se puede usar en maven para distinguir diferentes entornos. En gradle, podemos definir diferentes archivos de perfil y luego cargarlos a través de scripts:

build.gradle :

if (!hasProperty('buildProfile')) ext.buildProfile = 'default'  

apply from: "profile-${buildProfile}.gradle"  

task greeting {
    doLast {
        println message  
    }
}

profile-default.gradle :

ext.message = 'foobar'

profile-test.gradle :

ext.message = 'testing 1 2 3'

Podemos ejecutarlo así:

> gradle greeting
foobar

> gradle -PbuildProfile=test greeting
testing 1 2 3

Manejo de recursos

Hay una etapa de recursos de proceso en maven, que puede ejecutar recursos: recursos para copiar archivos de recursos.

La tarea processResources del complemento de Java en Gradle también puede hacer lo mismo.

Por ejemplo, puedo realizar la tarea de copia:

task copyReport(type: Copy) {
    from file("buildDir/reports/my-report.pdf")
    into file("buildDir/toArchive")
}

Copia más complicada:

task copyPdfReportsForArchiving(type: Copy) {
    from "buildDir/reports"
    include "*.pdf"
    into "buildDir/toArchive"
}

Por supuesto, existen aplicaciones más complejas para copiar. No lo explicaré en detalle aquí.

Supongo que te gusta

Origin blog.csdn.net/doubututou/article/details/112919208
Recomendado
Clasificación