Maven Concepts - Dependencies: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
(Created page with "=Internal= * Maven Concepts !!!The Dependency Mechanism Source article "Introduction to the Dependency Mechanism" [https://maven.apache.org/...")
 
 
(50 intermediate revisions by the same user not shown)
Line 1: Line 1:
=External=
* Introduction to the Dependency Mechanism https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html
=Internal=
=Internal=


* [[Maven Concepts#Dependencies|Maven Concepts]]
* [[Maven Concepts#Dependencies|Maven Concepts]]


!!!The Dependency Mechanism
=Dependency Operations=
 
<blockquote style="background-color: #f9f9f9; border: solid thin lightgrey;">
:[[Maven_dependency_Plugin#Operations|Maven dependency Plugin Operations]]
</blockquote>
 
=Overview=


Source article "Introduction to the Dependency Mechanism" [https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html]
=Declaration=


!!!Transitive Dependencies
Dependencies are declared inside a <dependencies> element. An individual dependency is declared inside a <dependency> element:
 
<dependencies>
    ...
    <dependency>
        <groupId>'''[[Maven_Concepts#Group_ID|groupId]]'''</groupId>
        <artifactId>'''[[Maven_Concepts#Artifact_ID|artifactId]]'''</artifactId>
        <version>'''[[Maven_Concepts#Version|version]]'''</version>
        <exclusions>
            <exclusion>
                <groupId></groupId>
                <artifactId></artifactId>
            <exclusion>
        </exclusions>
        <type>'''[[Maven_Concepts#Type|type]]'''</type>
        <scope>'''[[#Dependency_Scope|scope]]'''</scope>
    <dependency>
    ...
</dependencies>
 
=Transitive Dependencies=


Maven walks the dependency graph of your direct dependencies, allowing you to avoid the need to discover and specify those libraries by hand. This is possible because Maven reads the project files of the dependencies, recursively, from their repositories. There is no limit on the number of recursive levels Maven can walk.
Maven walks the dependency graph of your direct dependencies, allowing you to avoid the need to discover and specify those libraries by hand. This is possible because Maven reads the project files of the dependencies, recursively, from their repositories. There is no limit on the number of recursive levels Maven can walk.


Since the transitive dependency graph can grow pretty large, Maven offer a series of features that can be used to limit and manage the extent of the graph ([dependency mediation|MavenConcepts#DependencyMediation], [dependency management|MavenConcepts#DependencyManagement], [dependency scope|MavenConcepts#DependencyScope], [excluded dependencies|  MavenConcepts#ExcludedDependencies], [optional dependencies|MavenConcepts#OptionalDependencies]).
Since the transitive dependency graph can grow pretty large, Maven offer a series of features that can be used to limit and manage the extent of the graph ([[#Dependency_Mediation|dependency mediation]], [[#Dependency_Management|dependency management]], [[#Dependency_Scope|dependency scope]], [[#Excluded_Dependencies|excluded dependencies]], [[#Optional_Dependencies|optional dependencies]]).


!!!Dependency Mediation
{{Warn|The transitivity is interrupted when a dependency is declared [[#provided_is_not_transitive|<provided>]].}}
 
=Dependency Mediation=


Dependency mediation is used when multiple version of the same artifact are found while building the transitive graph.
Dependency mediation is used when multiple version of the same artifact are found while building the transitive graph.


The default mediation mechanism is "nearest definition", where the "closest" dependency in the graph dictates the version. However, a certain (different) version can be guaranteed by declaring it explicitly in the [dependency management|MavenConcepts#DependencyManagement] section of the  POM.
The default mediation mechanism is "nearest definition", where the "closest" dependency in the graph dictates the version. However, a certain (different) version can be guaranteed by declaring it explicitly in the [[#Dependency_Management|dependency management]] section of the  POM.


If two dependency versions are at the same depth, the declaration order counts: the first declaration wins.
If two dependency versions are at the same depth, the declaration order counts: the first declaration wins.


!!!Dependency Scope
=Dependency Scope=


The dependency scope is a declarative feature that allows you to only include dependencies appropriate for the current stage of the build. There are 6 scopes:
The dependency scope is a declarative feature that allows you to only include dependencies appropriate for the current stage of the build. It is declared inside the [[#Declaration|<dependency><scope>]] element. The scope is similar to
a [[Gradle_Dependencies_and_Dependency_Configurations#Configuration|Gradle configuration]].


!!compile
There are 6 scopes:
 
==compile==


The default scope, when none is specified. Compile dependencies are available in ''all'' classpaths of the project ''and'' propagated to dependent projects.
The default scope, when none is specified. Compile dependencies are available in ''all'' classpaths of the project ''and'' propagated to dependent projects.


!!provided
==provided==


Makes the dependencies available on the compilation and test class path, but does not includes the dependencies in the artifacts - because "provided" dependencies are assumed to be provided by the deployment environment. The provided scope is ''not'' transitive.
Makes the dependencies available on the compilation and test class path, but does not includes the dependencies in the artifacts - because "provided" dependencies are assumed to be provided by the deployment environment.  


!!runtime
<span id="provided_is_not_transitive"></span>The provided scope is ''not'' transitive, meaning that once a dependency was declared <provided>, ''its dependencies won't be transitively added to the classpath''.
 
==runtime==


Places the dependencies in the runtime and test classpaths, but not in the compile classpath.  
Places the dependencies in the runtime and test classpaths, but not in the compile classpath.  


!!test
==test==


This scope indicates that the dependency is not required for normal use of the application. The dependency is placed on the test compilation and test execution classpaths.
This scope indicates that the dependency is not required for normal use of the application. The dependency is placed on the test compilation and test execution classpaths.


!!system
==system==


Similar with "provided", except that the dependency artifact must be specified in the POM - it is not looked up in a repository:
Similar with "provided", except that the dependency artifact must be specified in the POM - it is not looked up in a repository:


{{{
<pre>
     ...
     ...
     <dependency>
     <dependency>
Line 65: Line 106:
     </dependency>
     </dependency>
     ...
     ...
}}}
</pre>


!!import
==import==


This scope is only used with a dependency of the type "pom" in &lt;dependencyManagement&gt; section. The effect is inclusion of the dependencies specified in the POM's &lt;dependencyManagement&gt; section. This is used to implement the [Bill of Materials (BOM) pattern|MavenConcepts#BOM]. <font color=red>Since they are replaced, dependencies with a scope of "import" do not participate in limiting the transitivity of a dependency.</font>
This scope is only used with a dependency of the type "pom" in <dependencyManagement> section. The effect is inclusion of the dependencies specified in the POM's <dependencyManagement> section. This is used to implement the [[#BOM|Bill of Materials (BOM) pattern]]. <font color=red>Since they are replaced, dependencies with a scope of "import" do not participate in limiting the transitivity of a dependency.</font>


!!!How Scopes Affect Transitivity
=How Scopes Affect Transitivity=


If a dependency is set to the scope in the left column, transitive dependencies of that dependency with the scope across the top row will result in a dependency in the main project with the scope listed at the intersection. No scope listed means the dependency will be omitted:
If a dependency is set to the scope in the left column, transitive dependencies of that dependency with the scope across the top row will result in a dependency in the main project with the scope listed at the intersection. No scope listed means the dependency will be omitted:


|               | compile | provided | runtime   | test
{|
|compile   | compile |       -     | runtime   | -
|| compile || provided || runtime || test
|provided | provided|       -     | provided | -
|-
|runtime   | runtime  |       -      | runtime   | -
| compile || compile || - || runtime || -
|test         | test       |       -     | test         | -
|-
 
| provided || provided || - || provided || -
 
|-
| runtime || runtime || - || runtime || -
|-
| test || test || - || test || -
|}


=Dependency Management=


!!!Dependency Management
If project POMs are declared hierarchically, a useful feature is dependency management, that allows dependency information inheritance amongst hierarchical projects. The dependency management is a Maven feature that provide the following functions:


The dependency management is a Maven feature that provide the following functions:
1. It allows for the '''centralization of the dependency information'''. The dependency management can be used to specify versions for dependencies whose version were not specified directly in the <dependencies> section.


1. It allows for the __centralization of the dependency information__. The dependency management can be used to specify versions for dependencies whose version were not specified directly in the &lt;dependencies&gt; section.  
2. '''Dependency information inheritance'''. When you have a set of projects that inherit a common parent you can put all the information about the dependencies in the <dependencyManagement> section of the common POM and have simpler references to artifacts in child POMs. If a dependency is incompletely declared in a child pom and it is not present in the parent's <dependencyManagement> section, the build will fail.


2. __Dependency information inheritance__. When you have a set of projects that inherit a common parent you can put all the information about the dependencies in the &lt;dependencyManagement&gt; section of the common POM and have simpler references to artifacts in child POMs.  
3. It allows project authors to declare '''specific versions for their dependencies, when those dependencies are encountered in the transitive dependency graph''' (you can see it as a mask that applies to the transitive dependency graph).


3. It allows project authors to declare __specific versions for their dependencies, when those dependencies are encountered in the transitive dependency graph__ (you can see it as a mask that applies to the transitive dependency graph).
Configuration:


<blockquote style="background-color: #f9f9f9; border: solid thin lightgrey;">
:[[Maven_pom.xml#.3CdependencyManagement.3E|<dependencyManagement>]]
</blockquote>


=Excluded Dependencies=


!!!Excluded Dependencies
Dependencies that are pulled in with the transitive dependency graph can be explicitly excluded with the <exclusion> element.


Dependencies that are pulled in with the transitive dependency graph can be explicitly excluded with the &lt;exclusion&gt; element.
=Optional Dependencies=
 
 
!!!Optional Dependencies


Optional dependencies do are not included in the transitive dependency graph. Optional means "excluded by default".
Optional dependencies do are not included in the transitive dependency graph. Optional means "excluded by default".


=BOM=


BOM ("bill of materials") is a pattern that allow projects to import managed dependencies from another project.


!!!BOM
This is accomplished by declaring a special BOM artifact, as described below, which is essentially a versioned list of dependencies. The BOM specifies a dependency graph to chose from, not a dependency. The concrete dependencies still need to be declared, but with a BOM, Maven will prioritize artifacts specified by the BOM over other artifacts, when resolving transitive dependencies.


BOM (Bill of Material) is a pattern that allow projects to import managed dependencies from another project. This is accomplished by declaring a "pom" artifact as a dependency with the "import" scope (the bill of materials). The bill of materials is essentially versioning a list of dependencies.
==BOM Declaration==


A project ("bom") can declare itself a "bill of materials" and it defines the versions of the artifacts that will be created in the library of artifacts. This effectively incapsulates a set of versions and gives a unique version to the incapsulated list. Other project (X) can import it, thus importing all dependencies declared in the bill of material.
A project ("bom-exporter") can declare a "bill of materials" that specifies versions for a set of artifacts. Usually, these artifacts have been tested together, and they are known to work well together. This effectively incapsulates the set of versions and associates an unique version to the incapsulated list. The BOM is declared as a "pom"-packaged artifact.


Also see [import scope|MavenConcepts#import].
This is an example of a BOM declaration:


"bom" POM:
<syntaxhighlight lang='xml'>
 
{{{
<project ...>
<project ...>
   <groupId>com.test</groupId>
   <groupId>com.test</groupId>
   <artifactId>bom</artifactId>
   <artifactId>bom</artifactId>
   <version>1.0.0</version>
   <version>1.0</version>
   <packaging>pom</packaging>
   <packaging>pom</packaging>
   <properties>
   <properties>
     <project1Version>1.0.0</project1Version>
     <project1Version>2.0</project1Version>
     <project2Version>1.0.0</project2Version>
     <project2Version>3.0</project2Version>
   </properties>
   </properties>
   <dependencyManagement>
   <dependencyManagement>
Line 137: Line 184:
         <groupId>com.test</groupId>
         <groupId>com.test</groupId>
         <artifactId>project2</artifactId>
         <artifactId>project2</artifactId>
         <version>${project1Version}</version>
         <version>${project2Version}</version>
       </dependency>
       </dependency>
     </dependencies>
     </dependencies>
   </dependencyManagement>
   </dependencyManagement>
</project>
</project>
}}}
</syntaxhighlight>
 
The BOMs are usually assembled by vendors. An example of vendor-assembled BOMs are the [[JBoss EAP BOMs#Overview|JBoss EAP BOMs]].
 
==Using the BOM==
 
A dependent project declares is intention to use one or more dependencies declared in the BOM by:
# declaring the BOM dependency with an "[[#import|import scope]]" scope and specifying a BOM version in its <dependencyManagement> section of the POM. Note that the declaration inside the <dependencyManagement> section is essential, BOM import won't work otherwise.
# declaring specific dependencies in the <dependencies> section of the POM, without specifying any version - the version will be inferred transitively based on versioning information provided by the BOM.


X POM:
The declaration of the BOM in <dependencyManagement> introduces the dependency graph declared by the BOM into the current project dependency resolution algorithm.


{{{
<syntaxhighlight lang='xml'>
<project ...>
<project ...>
   <groupId>X</groupId>
   <groupId>X</groupId>
Line 158: Line 213:
         <groupId>com.test</groupId>
         <groupId>com.test</groupId>
         <artifactId>bom</artifactId>
         <artifactId>bom</artifactId>
         <version>1.0.0</version>
         <version>1.0</version>
         <type>pom</type>
         <type>pom</type>
         <scope>import</scope>
         <scope>import</scope>
Line 175: Line 230:
   </dependencies>
   </dependencies>
</project>
</project>
}}}
</syntaxhighlight>
 
Example of using a JBoss BOM:
 
<syntaxhighlight lang='xml'>
<project ...>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.jboss.bom</groupId>
        <artifactId>eap6-supported-artifacts</artifactId>
        <version>6.4.15.GA</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
 
  <dependencies>
    <dependency>
      <groupId>org.jboss.spec.javax.servlet</groupId>
      <artifactId>jboss-servlet-api_3.0_spec</artifactId>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>
</syntaxhighlight>
 
Related:
 
{{Internal|JBoss EAP BOMs|JBoss EAP BOMs}}

Latest revision as of 03:57, 28 March 2021

External

Internal

Dependency Operations

Maven dependency Plugin Operations

Overview

Declaration

Dependencies are declared inside a <dependencies> element. An individual dependency is declared inside a <dependency> element:

<dependencies>

    ...

    <dependency>
        <groupId>groupId</groupId>
        <artifactId>artifactId</artifactId>
        <version>version</version>
        <exclusions>
            <exclusion>
                <groupId></groupId>
                <artifactId></artifactId>
            <exclusion>
        </exclusions>
        <type>type</type>
        <scope>scope</scope>
    <dependency>

   ...

</dependencies>

Transitive Dependencies

Maven walks the dependency graph of your direct dependencies, allowing you to avoid the need to discover and specify those libraries by hand. This is possible because Maven reads the project files of the dependencies, recursively, from their repositories. There is no limit on the number of recursive levels Maven can walk.

Since the transitive dependency graph can grow pretty large, Maven offer a series of features that can be used to limit and manage the extent of the graph (dependency mediation, dependency management, dependency scope, excluded dependencies, optional dependencies).


The transitivity is interrupted when a dependency is declared <provided>.

Dependency Mediation

Dependency mediation is used when multiple version of the same artifact are found while building the transitive graph.

The default mediation mechanism is "nearest definition", where the "closest" dependency in the graph dictates the version. However, a certain (different) version can be guaranteed by declaring it explicitly in the dependency management section of the POM.

If two dependency versions are at the same depth, the declaration order counts: the first declaration wins.

Dependency Scope

The dependency scope is a declarative feature that allows you to only include dependencies appropriate for the current stage of the build. It is declared inside the <dependency><scope> element. The scope is similar to a Gradle configuration.

There are 6 scopes:

compile

The default scope, when none is specified. Compile dependencies are available in all classpaths of the project and propagated to dependent projects.

provided

Makes the dependencies available on the compilation and test class path, but does not includes the dependencies in the artifacts - because "provided" dependencies are assumed to be provided by the deployment environment.

The provided scope is not transitive, meaning that once a dependency was declared <provided>, its dependencies won't be transitively added to the classpath.

runtime

Places the dependencies in the runtime and test classpaths, but not in the compile classpath.

test

This scope indicates that the dependency is not required for normal use of the application. The dependency is placed on the test compilation and test execution classpaths.

system

Similar with "provided", except that the dependency artifact must be specified in the POM - it is not looked up in a repository:

     ...
    <dependency>
        <groupId>javax.sql</groupId> 
        <artifactId>jdbc-stdext</artifactId>
        <version>2.0</version>
        <scope>system</scope> 
        <systemPath>${java.home}/lib/rt.jar</systemPath>
    </dependency> 

    ...

    <dependency>
        <groupId>sample</groupId>
        <artifactId>com.sample</artifactId>
        <version>1.0</version>
        <scope>system</scope>
        <systemPath>${project.basedir}/src/main/resources/yourJar.jar</systemPath>
    </dependency>
    ...

import

This scope is only used with a dependency of the type "pom" in <dependencyManagement> section. The effect is inclusion of the dependencies specified in the POM's <dependencyManagement> section. This is used to implement the Bill of Materials (BOM) pattern. Since they are replaced, dependencies with a scope of "import" do not participate in limiting the transitivity of a dependency.

How Scopes Affect Transitivity

If a dependency is set to the scope in the left column, transitive dependencies of that dependency with the scope across the top row will result in a dependency in the main project with the scope listed at the intersection. No scope listed means the dependency will be omitted:

compile provided runtime test
compile compile - runtime -
provided provided - provided -
runtime runtime - runtime -
test test - test -

Dependency Management

If project POMs are declared hierarchically, a useful feature is dependency management, that allows dependency information inheritance amongst hierarchical projects. The dependency management is a Maven feature that provide the following functions:

1. It allows for the centralization of the dependency information. The dependency management can be used to specify versions for dependencies whose version were not specified directly in the <dependencies> section.

2. Dependency information inheritance. When you have a set of projects that inherit a common parent you can put all the information about the dependencies in the <dependencyManagement> section of the common POM and have simpler references to artifacts in child POMs. If a dependency is incompletely declared in a child pom and it is not present in the parent's <dependencyManagement> section, the build will fail.

3. It allows project authors to declare specific versions for their dependencies, when those dependencies are encountered in the transitive dependency graph (you can see it as a mask that applies to the transitive dependency graph).

Configuration:

<dependencyManagement>

Excluded Dependencies

Dependencies that are pulled in with the transitive dependency graph can be explicitly excluded with the <exclusion> element.

Optional Dependencies

Optional dependencies do are not included in the transitive dependency graph. Optional means "excluded by default".

BOM

BOM ("bill of materials") is a pattern that allow projects to import managed dependencies from another project.

This is accomplished by declaring a special BOM artifact, as described below, which is essentially a versioned list of dependencies. The BOM specifies a dependency graph to chose from, not a dependency. The concrete dependencies still need to be declared, but with a BOM, Maven will prioritize artifacts specified by the BOM over other artifacts, when resolving transitive dependencies.

BOM Declaration

A project ("bom-exporter") can declare a "bill of materials" that specifies versions for a set of artifacts. Usually, these artifacts have been tested together, and they are known to work well together. This effectively incapsulates the set of versions and associates an unique version to the incapsulated list. The BOM is declared as a "pom"-packaged artifact.

This is an example of a BOM declaration:

<project ...>
  <groupId>com.test</groupId>
  <artifactId>bom</artifactId>
  <version>1.0</version>
  <packaging>pom</packaging>
  <properties>
    <project1Version>2.0</project1Version>
    <project2Version>3.0</project2Version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.test</groupId>
        <artifactId>project1</artifactId>
        <version>${project1Version}</version>
      </dependency>
      <dependency>
        <groupId>com.test</groupId>
        <artifactId>project2</artifactId>
        <version>${project2Version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>

The BOMs are usually assembled by vendors. An example of vendor-assembled BOMs are the JBoss EAP BOMs.

Using the BOM

A dependent project declares is intention to use one or more dependencies declared in the BOM by:

  1. declaring the BOM dependency with an "import scope" scope and specifying a BOM version in its <dependencyManagement> section of the POM. Note that the declaration inside the <dependencyManagement> section is essential, BOM import won't work otherwise.
  2. declaring specific dependencies in the <dependencies> section of the POM, without specifying any version - the version will be inferred transitively based on versioning information provided by the BOM.

The declaration of the BOM in <dependencyManagement> introduces the dependency graph declared by the BOM into the current project dependency resolution algorithm.

<project ...>
  <groupId>X</groupId>
  <artifactId>x</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>
 
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.test</groupId>
        <artifactId>bom</artifactId>
        <version>1.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.test</groupId>
      <artifactId>project1</artifactId>
    </dependency>
    <dependency>
      <groupId>com.test</groupId>
      <artifactId>project2</artifactId>
    </dependency>
  </dependencies>
</project>

Example of using a JBoss BOM:

<project ...>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.jboss.bom</groupId>
         <artifactId>eap6-supported-artifacts</artifactId>
         <version>6.4.15.GA</version>
         <type>pom</type>
         <scope>import</scope>
       </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>org.jboss.spec.javax.servlet</groupId>
      <artifactId>jboss-servlet-api_3.0_spec</artifactId>
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

Related:

JBoss EAP BOMs