descripción de la dependencia maven

¿Por qué quieres escribir este artículo?

El problema de seguridad del núcleo Log4j del año pasado llevó una vez más la seguridad de la cadena de suministro a un clímax. En el contexto de la seguridad de la cadena de suministro, los dos productos de Ant Group, la plataforma de escaneo de códigos estáticos (STC) y la plataforma de perspectiva de amenazas de activos (Hubble), complementan las ventajas de cada uno y resuelven eficazmente los escenarios de dependencia directa e indirecta.

Sin embargo, dado que STC se basa en ex ante, existe el riesgo de que se pierda debido a limitaciones en la eficiencia del escaneo, mientras que Hubble se basa en ex post, y existe el riesgo de que se requiera tiempo de reparación. En base a esto, el autor intentó encontrar una manera de solucionar las deficiencias de los dos productos al mismo tiempo. El autor intentó estudiar cómo Maven maneja las dependencias directas e indirectas en un proyecto, y cómo Maven toma una decisión cuando encuentra la misma dependencia. La decisión aquí es en realidad el mecanismo de arbitraje de Maven. Con estas preguntas en mente, el autor intentó investigar el código fuente de Maven e hizo algunos experimentos de prueba locales. Eso concluye el artículo.

¿Cuáles son las coordenadas?

En el sistema de coordenadas espaciales, podemos representar un punto a través de xyz, de manera similar, en el mundo Maven, podemos representar claramente una dependencia en el mundo dependiente a través de un conjunto de GAV, como por ejemplo:
 

<groupId> : com.alibaba 一般是公司的名称

<artifactId> : fastjson 项目名称

<version> : 1.2.24 版本号

¿Cuáles son las etiquetas que afectan las dependencias?

1.<dependencias>

Introduzca directamente información de dependencia específica. Tenga en cuenta que no está dentro de la etiqueta <dependencyManagement>. Si está dentro de <dependencyManagement>, consulte la etiqueta No. 2.

2.<gestión de dependencia>

Sólo se produce una declaración pero no una introducción real, como gestión de dependencias. La gestión de dependencias se refiere a hacer referencia a los datos de gestión de dependencias cuando las dependencias realmente ocurren.

  • Cuando utilice la dependencia de esta manera, puede utilizar la versión de forma predeterminada.

  • Además, <dependencyManagement> también puede controlar todas las dependencias indirectas, incluso si las dependencias indirectas declaran una versión, se sobrescribirá.

3.<padre>

Para declarar a mi padre, la filosofía de herencia de Maven es muy similar a la de Java, porque el propio Maven también está implementado en Java y satisface la herencia única.

  • Una vez que el pom secundario hereda el pom principal, heredará las <dependencias>, <dependencyManagement> y otros atributos en el pom principal. Por supuesto, si el mismo elemento aparece durante el proceso de herencia, el hijo sobrescribirá al padre, similar a Java.

  • Al heredar, se clasifica la herencia. Las dependencias heredan dependencias, y la gestión de dependencias en dependencyManagement solo puede heredar la gestión de dependencias dentro del alcance de dependencyManagement.

  • Cada archivo pom tendrá un padre, incluso si no se declara Parent, habrá un padre por defecto. Es similar a la filosofía de diseño de objetos de Java. Lo mencionaremos más adelante en el análisis del código fuente.

4.<propiedades>

Una colección de propiedades que representan el proyecto propio actual.

Las propiedades solo representan la declaración de propiedades, una vez que se declara una propiedad, no tiene nada que ver con si se hace referencia a ella. Puedo declarar una serie de propiedades que no son utilizadas por nadie.

¿Cuáles son los ámbitos dependientes?

Cuando se introduce una dependencia, se puede declarar el alcance de la dependencia. Por ejemplo, esta dependencia solo funciona localmente, por ejemplo, solo funciona para pruebas, etc. El alcance tiene un total de valores de compilación, proporcionados, sistema, prueba, importación y tiempo de ejecución.

Para resumir brevemente:

  • La compilación y el tiempo de ejecución participarán en el proceso de empaquetado final, y el resto no. No es necesario escribir compilar.

  • test solo funcionará en el código de prueba en el directorio src/test.

  • Proporcionado significa que el paquete Jar se ha proporcionado en línea y no es necesario considerarlo al empaquetar. Generalmente, se proporcionan muchos paquetes, como servlets.

  • No hay mucha diferencia entre el sistema y lo proporcionado.

  • La importación solo aparecerá en las dependencias dentro de la etiqueta dependencyManagement para resolver la herencia única de Maven. Si se introduce este alcance, Maven cargará todos los elementos en la gestión de dependencias de esta dependencia en el pom actual, pero no introducirá el nodo actual. Como se muestra en la figura siguiente, fastjson no se introducirá como un elemento de gestión de dependencias, pero se introducirá la gestión de dependencias definida por el archivo fastjson.

<dependencyManagement><dependencies>  <dependency>    <groupId>com.alibaba</groupId>    <artifactId>fastjson</artifactId>    <version>1.2.24</version>    <scope>import</scope>  </dependency><dependencies><dependencyManagement>

2. Competencia de dependencia en un solo árbol Pom

Esencia de lima pom

La esencia de un archivo Pom es un árbol.

Cuando observamos un archivo Pom desde una perspectiva humana, lo consideraremos como una lista de dependencia lineal. Pensaremos que el resultado abstracto del archivo Pom en la siguiente figura es que C depende de A, B y D. Pero nuestra perspectiva está incompleta: desde la perspectiva de Maven, Maven abstraerá directamente este archivo Pom en un árbol de dependencia. La perspectiva de Maven puede ver nodos excepto ABD. La gente sólo puede ver los tres nodos ABD.

Como está en un árbol, debe haber competencia entre los mismos nodos. Esta relación competitiva es lo que mencionamos como mecanismo de arbitraje.

Principios del mecanismo de arbitraje de Maven

1. Cuando se dependa de la competencia, se dará prioridad al que esté más cerca del tronco principal.

2. Cuando un solo árbol depende de la competencia (dependencias) (nota: no dependencias en dependencyManagement):

Cuando profundidad = 1, depende directamente. Se da prioridad a aquellos que están en el mismo nivel.

Cuando profundidad>1, significa dependencia indirecta. Se da prioridad a aquellos que están en el mismo nivel.

3. Un solo árbol tiene prioridad cuando compite la gestión de dependencias (nota: son dependencias en dependencyManagement).

4. Las dos relaciones más importantes en Maven son la herencia y la dependencia. Todas nuestras leyes sólo deberían partir de estas dos relaciones.

    La siguiente figura muestra dos archivos subpom respectivamente (el cuadrado representa el nodo dependiente, A-1 significa que el nodo A usa la versión 1, la letra representa el nodo y el número representa la versión).

    El árbol generado por el subpom de la izquierda depende de D-1, D-2 y D-5. Satisface el principio 1 de competencia de dependencia, es decir, el principio de que cuanto más cerca del lado izquierdo del árbol, mayor será la prioridad, por lo que D-5 competirá con éxito.

    Pero B-1 y B-2 están ubicados a la misma profundidad del árbol al mismo tiempo, y la profundidad es 1. Como B-2 está más atrás, B-2 competirá con éxito.

    El árbol generado por el subpom de la derecha depende de D-1 y D-2 y está ubicado a la misma profundidad. Sin embargo, dado que D-1 y D-2 pertenecen al alcance de la dependencia indirecta, la profundidad es mayor que 1, se le da prioridad al frente, por lo que también es decir, D-1 competirá con éxito.

Escenarios comunes

Al ver esto, debes haber entendido los principios de arbitraje de Maven. Pero en el trabajo real, solo tienes principios y necesitas poder aplicarlos de manera flexible en el código para tener tu propia comprensión. Aquí he preparado 5 escenarios y las respuestas a cada escenario están al final. Cuando lees, Puede intentar utilizar los principios de Maven para razonar y ver si hay algo que no cumpla con las expectativas.

Escenario 1 dificultad (*)

descripción de la escena

Hay <fastjson.version> en el POM principal. Este atributo es 1.2.24.

El padre es spring-boot-starter-parent-3.13.0. La <fastjson.version> en el padre es 1.2.77.

Y en el pompón principal se consume este atributo.

Entonces, para el árbol POM principal, ¿qué fastjson utilizará finalmente?

Ejemplo de escenario

Diagrama de estructura

Respuesta:

1.2.24 finalmente entrará en vigor.

Porque el niño heredará los atributos del padre, pero como tiene este atributo, ¡se sobrescribirá!

La herencia definitivamente irá acompañada de sobrescritura. Este diseño es relativamente común en los lenguajes de programación.

Escenario 2 dificultad (**)

Fastjson se usa en dependencias en el mismo POM principal o sub-POM, el primero declara la versión 1.2.24 y el segundo declara la versión 1.2.25. Entonces, para el árbol POM principal o subpom, ¿elegirá finalmente fastjson 1.2.24 o 1.2.25?

Ejemplo de escenario

Diagrama de estructura

Respuesta:

1.2.25 finalmente entrará en vigor.

Consulte un solo árbol cuando las dependencias compitan: cuando profundidad = 1, es directamente dependiente. Se da prioridad a aquellos que están en el mismo nivel.

¡Conozca la estrategia central de dependencia competitiva de Maven!

Escenario Tres Dificultad (***)

En la imagen de la izquierda a continuación, el fastjson en dependencyManagement en el archivo POM principal es 1.2.77. En este momento, el POM secundario muestra su propia versión 1.2.78. Entonces, para el árbol infantil POM, ¿el niño POM elegirá obedecer las órdenes de su padre o seguir su corazón?

Ejemplo de escenario

Diagrama de estructura

Respuesta:

1.2.78 finalmente entrará en vigor.

¡La gestión de dependencias en un proyecto solo puede ser efectiva para dependencias y dependencias indirectas que no declaran una versión!

Escenario cuatro dificultad (****)

Principales dependencias de POM Fastjson:1.2.24 Principal administrador de dependencias de POM Fastjson:1.2.77

Dependencias principales de POM (springboot) Fastjson 1.2.78

dependencias en POM secundario Fastjson 1.2.25

En este caso, para el subpom, ¿cuál de las cuatro versiones elegirá?

Ejemplo de escenario

Diagrama de estructura

Respuesta:

1.2.25 finalmente entrará en vigor. Esto es más complicado.

〇: Primero, según la relación de herencia entre padre e hijo, 1.2.24 sobrescribirá 1.2.78. Entonces se elimina la versión 78.

1: Dado que la gestión de dependencias en un proyecto solo puede ser efectiva para dependencias y dependencias indirectas que no declaran la versión, entonces

1.2.77 no funcionará en 1.2.25.

Dos: debido a la relación de herencia entre padre e hijo, 1.2.25 sobrescribirá 1.2.24.

¡Así que al final ganó 1.2.25!

Escenario 5 dificultad (*****)

Principales dependencias de POM Fastjson:1.2.24 Principal administrador de dependencias de POM Fastjson:1.2.77

Dependencias principales de POM (springboot) Fastjson 1.2.78

Las dependencias en el sub-POM no escriben la versión.

No existe una diferencia general entre el escenario cinco y el escenario cuatro, excepto que la versión de dependencias del subpom está predeterminada.

En este caso, para el subpom, ¿cuál de las tres versiones elegirá?

Ejemplo de escenario

Diagrama de estructura

Respuesta:

1.2.77 finalmente entrará en vigor.

〇: Primero, según la relación de herencia entre padre e hijo, 1.2.24 sobrescribirá 1.2.78. Entonces se elimina la versión 78.

1: Dado que dependencyManagement en un proyecto puede funcionar en versiones no declaradas, la versión del subpom es 1.2.77

Dos: debido a la relación de herencia entre padre e hijo, 1.2.77 sobrescribirá 1.2.24.

¡Así que al final ganó 1.2.77!

Fusiona y empaqueta tres o más árboles Pom

Principios de secuencia de construcción de árboles múltiples

Los proyectos actuales generalmente se administran mediante varios módulos y habrá muchos archivos pom. En el caso de varios árboles, el orden de aparición de cada árbol se ha calculado de antemano.

Esta función se implementa en el código fuente de Maven mediante un dispositivo llamado Reactor. Una de las cosas principales que hace es determinar el orden en el que se construirán primero varios subpoms en un proyecto. Este orden de fábrica es muy importante. Al fusionar y empaquetar, a menudo determina quién ganará entre varios poms al final. .

Principios de los reactores

El orden en que se construyen varios árboles (múltiples subpoms) se basa en el principio de que la parte dependiente primero y la parte dependiente al final.

El proyecto debe garantizar que aquí no haya dependencias circulares.

Diagrama de principio del reactor

Como se muestra en la figura siguiente, sub-pom1 depende tanto de sub-pom2 como de sub-pom3, por lo que sub-pom1 se construye primero y sub-pom3 no tiene a nadie que dependa de él, por lo que se construye al final.

Estrategia de empaquetado SpringBoot Fatjar

SpringBoot se empaquetará en un Fatjar y todas las dependencias se colocarán en el directorio BOOT-INF/lib/. Para el empaquetado SpringBoot, cuanto más tarde se construya el POM, mayor será la prioridad, porque el complemento del empaquetado Springboot generalmente se coloca en el módulo menos dependiente (como Pom3 en la imagen de arriba). (El complemento de empaquetado de SpringBoot generalmente se coloca en el pom de arranque. Podemos elegir este nombre nosotros mismos y, por lo general, es el módulo con la mayor dependencia. En una aplicación Springboot administrada por varios módulos, el arranque suele ser el módulo menos confiable. en. )

El sub-pom3 finalmente participa en la construcción y el complemento de empaquetado SpringBoot generalmente usa este módulo. Entonces los que finalmente ingresaron a los productos empaquetados SpringBoot son A-2, B-2, E-2, F-2 y D-1. Porque A-2 y B-2 están más cerca del tronco del árbol que varios otros nodos idénticos. Lo mismo ocurre con E-2 y F-2. Esta regla da prioridad a la parte trasera, porque los árboles de la parte trasera están naturalmente más cerca del tronco.

Implementación de cuatro mecanismos de arbitraje en el código fuente de Maven.

Utilizando el código fuente de Maven versión 3.6.3 para el análisis, intentamos analizar varios principios del procesamiento de dependencias en Maven, de modo que podamos probar positivamente la precisión del mecanismo de arbitraje desde el nivel del código fuente. Además, también puede ver en el código fuente por qué algunos mecanismos en Maven son así, en lugar de simplemente cuál es su mecanismo. Debido a que el autor cree que ningún mecanismo puede garantizar el avance de los tiempos, el autor cree que todos los mecanismos de arbitraje mencionados anteriormente pueden cambiar algún día. Estas conclusiones no son las más importantes, pero cómo investigar estas conclusiones es más importante. Porque ¡es importante!

¿Cómo implementa Maven la herencia y permite que el hijo sobrescriba al padre con los mismos atributos?

Hay 2 líneas principales muy importantes en Maven. Una es dependencia y la otra es herencia. Maven implementa la herencia en el código fuente de la siguiente manera. Después de usar readParent para obtener el modelo del padre en la siguiente figura, caes en este bucle. La única manera de salir de este ciclo es hasta que no puedas alcanzar a tu padre. Y coloque los datos del modelo obtenidos cada vez en el objeto linega. En el ensamblado inferior de Herencia en la figura siguiente, vemos que consume el objeto linega, con el propósito de completar la herencia real y sobrescribir.

Encontraremos un fenómeno muy interesante en el ensamblaje. Herencia: Lingage se recorre hacia atrás y comienza desde el penúltimo elemento. Esta es exactamente una de las filosofías de diseño de Maven que mencionamos anteriormente. Maven cree que todos los archivos pom del mundo tienen un padre, similar al objeto de Java. He aquí una lógica simple de este tratamiento filosófico.

Además, Maven atraviesa de arriba a abajo, lo que hace que sea más conveniente para usted darse cuenta de la capacidad del mismo elemento secundario para sobrescribir al padre. Esta también es una pequeña idea de codificación que piensa el autor.

Implementación de Reactor reactor en código fuente.

También mencionamos arriba un concepto muy importante que es el reactor. El reactor determina directamente cómo cada subpom determina el orden de construcción. En el código fuente de Maven, se implementa en la función getProjectsForMavenReactor. ¡Y también podemos ver en la imagen a continuación que el reactor de Maven no puede resolver dependencias circulares y detecta directamente esta excepción!

La implementación real del algoritmo del reactor se implementa a través de Dag en el constructor de ProjectSorter. Dag (gráfico acíclico dirigido) y la búsqueda en amplitud son una buena forma de resolver escenarios de dependencia.

En el gráfico acíclico dirigido, cada vez se selecciona el nodo con un grado de entrada y salida de 0, luego se eliminan el nodo y sus bordes adyacentes y se repiten los pasos anteriores. Puede calcular de manera eficiente el orden de dependencia de todos los nodos en el DAG. Maven también usa esta idea.

Desde la perspectiva de este código fuente, también se puede explicar por qué Maven debe garantizar que no aparezcan dependencias circulares antes de cada subpom.

La implementación prioritaria declarada después de la dependencia en el mismo archivo Pom.

Cuando se trata de dependencias, Maven no realiza un procesamiento especial y utiliza directamente el método Map para sobrescribirlo. No sé por qué está diseñado así. El autor especuló una vez que este diseño era para permitir que los estudiantes en desarrollo escribieran mejor, porque la última prioridad suele estar en línea con los hábitos de codificación de la mayoría de las personas. Pero aquí vemos una línea de comentarios del autor, lo que probablemente significa que este diseño es para compatibilidad con versiones anteriores de Maven2.x, porque Maven2.x no verificará si un archivo tiene solo una dependencia única con el mismo GA. Por lo tanto, las versiones posteriores de Maven también deberían continuar con este estilo.

Cuando el bucle se procesa a 1.2.25, la operación de colocación aún se realiza en el mapa normalizado, lo que resulta en una sobrescritura cuando los valores clave son los mismos.

Cinco perspectivas de seguridad: cómo evitar la dependencia indirecta

analizar

Como estudiante de seguridad, el autor espera poder resolver el problema de la dependencia indirecta para este tipo de proyecto Maven de múltiples módulos.

Tras el análisis anterior, podemos sacar 3 conclusiones:

1. La versión declarada de child pom es muy peligrosa desde una perspectiva de seguridad y child pom no debería mostrar la versión declarada.

Porque el pom secundario heredará los elementos del pom principal y se producirán escenarios de sobrescritura durante la herencia. Luego, al empaquetar para CE o SpringBoot, es posible que la posición del pedido de la compilación sub-pom sea naturalmente muy ventajosa, y es fácil hacer que la versión sub-pom ingrese al producto empaquetado final.

2. El dependencyManagent del POM principal puede controlar dependencias indirectas y dependencias directas que no declaran explícitamente versiones.

3. Las versiones peligrosas no pueden aparecer en las dependencias del POM principal. De lo contrario, el niño pom heredará naturalmente esta versión peligrosa y participará en el embalaje.

en conclusión

Si se cumplen las condiciones anteriores al mismo tiempo, se puede resolver el problema de la dependencia indirecta.

Ahora mismo:

Para SpringBoot, el pom secundario no debe mostrar la versión declarada, el dependencyManagent del pom principal debe controlar las dependencias de la versión segura y el pom principal no debe tener versiones peligrosas. (Es mejor forzar que las dependencias principales de Pom escriban una versión segura, para evitar dependencias residuales inseguras en el padre dependiente)

seis últimos

Dirección del código fuente de Maven

https://archive.apache.org/dist/maven/maven-3/

¿Cómo analicé?

He realizado varias rondas de pruebas en SpringBoot localmente. ¡Simplemente ejecute el paquete mvn clean en el directorio raíz!

mvn clean org.apache.maven.plugins:maven-dependency-plugin:3.3.0:tree -Dverbose=true ayudará a analizar nodos específicos.

¡La otra cosa es tratar de encontrar la implementación aquí en el código fuente para profundizar la comprensión!

Comandos de análisis más utilizados

0. Paquete mvn clean: DSkipTest empaqueta y analiza directamente los resultados

1. Dependencia de mvn: el árbol generará toda la estructura del árbol de Maven

2.mvn ayuda: Effective-pom -Dverbose Este comando genera información más completa y el resultado es Effectivepom.

3.mvn clean org.apache.maven.plugins:maven-dependency-plugin:3.3.0:tree -Dverbose=true

4.mvn -D maven.repo.local = dependencias utilizadas en la fase de compilación de su directorio.

Supongo que te gusta

Origin blog.csdn.net/qq_16504067/article/details/129882812
Recomendado
Clasificación