Author: MJ Carreon
With the introduction of transitive
dependencies in Maven 2.0, it became possible to simplify a POM by
including only the dependencies you need directly, and allowing Maven to
calculate the full dependency graph. However, as the graph grows, it is
inevitable that two or more artifacts will require different versions
of a particular dependency. In this case, Maven must choose which
version to provide.
In Maven, the version selected is the one declared “nearest” to the
top of the tree – that is, Maven selects the version that requires the
least number of dependencies to be traversed. A dependency in the POM
being built will be used over anything else. However, this has
limitations:
- The version chosen may not have all the features required by the
other dependencies.
- If multiple versions are selected at the same depth, then the result
is undefined.
While further dependency management features are scheduled for the
next release of Maven at the time of writing, there are ways to manually
resolve these conflicts as the end user of a dependency, and more
importantly ways to avoid it as the author of a reusable library.
To manually resolve conflicts, you can remove the incorrect version
from the tree, or you can override both with the correct version.
Removing the incorrect version requires identifying the source of the
incorrect version by running Maven with the -X
flag (for
more information on how to do this, see section 6.9 in Chapter 6). For
example, if you run mvn -X
test on the proficio-core
module, the output will contain something similar to:
proficio-core:1.0-SNAPSHOT
junit:3.8.1 (selected for test)
plexus-container-default:1.0-alpha-9 (selected for compile)
plexus-utils:1.0.4 (selected for compile)
classworlds:1.1-alpha-2 (selected for compile)
junit:3.8.1 (not setting scope to compile; local scope test wins)
proficio-api:1.0-SNAPSHOT (selected for compile)
proficio-model:1.0-SNAPSHOT (selected for compile)
plexus-utils:1.1 (selected for compile)
It
should be noted that running mvn -X
test depends on other
parts of the build having been executed beforehand, so it is useful to
run mvn install
at the top level of the project (in the
proficio directory)to ensure that needed components are installed into
the local repository.
Once the path to the version has been identified, you can exclude the
dependency from the graph by adding an exclusion to the dependency that
introduced it. In this example, plexus-utils
occurs twice,
and Proficio requires version 1.1 be used. To ensure this, modify the
plexus-container-default dependency in the proficio-core/pom.xml
file as follows:
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-container-default</artifactId>
<version>1.0-alpha-9</version>
<exclusions>
<exclusion>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
</exclusion>
</exclusions>
</dependency>
This
ensures that Maven ignores the 1.0.4 version of plexus-utils
in the dependency graph, so that the 1.1 version is used instead.
The alternate way to ensure that a particular version of a dependency
is used, is to include it directly in the POM
,
as follows:
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<version>1.1</version>
<scope>runtime</scope>
</dependency>
</dependencies>
However,
this approach is not recommended unless you are producing an artifact
that is bundling its dependencies and is not used as a dependency itself
(for example, a WAR
file). The reason for
this is that it distorts the true dependency graph, which will
accumulate if this project is reused as a dependency itself.
You’ll notice that the runtime scope is used here. This is because,
in this situation, the dependency is used only for packaging, not for
compilation. In fact, if the dependency were required for compilation,
for stability it would always be declared in the current POM
as a dependency – regardless of whether another
dependency introduces it.
Neither of these solutions is ideal, but it is possible to improve
the quality of your own dependencies to reduce the risk of these issues
occurring with your own build artifacts. This is extremely important if
you are publishing a build, for a library or framework, that will be
used widely by others. To accomplish this, use version ranges instead.
When a version is declared as 1.1
, as shown above for plexus-utils
,
this indicates that the preferred version of the dependency 1.1
,
but that other versions may be acceptable. Maven has no knowledge
regarding which versions will work, so in the case of a conflict with
another dependency, Maven assumes that all versions are valid and uses
the “nearest dependency” technique described previously to determine
which version to use.
However, you may require a feature that was introduced in plexus-utils
version 1.1. In this case, the dependency should be specified as
follows:
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-utils</artifactId>
<version>[1.1,)</version>
</dependency>
What
this means is that, while the nearest dependency technique will still
be used in the case of a conflict, the version that is used must fit the
range given. If the nearest version does not match, then the next
nearest will be tested, and so on. Finally, if none of them match, or
there were no conflicts originally, the version you are left with is [1.1,)
.
This means that the latest version, which is greater than or equal to
1.1, will be retrieved from the repository.
The notation used above is set notation, and the table below shows
some of the values that can be used.
Table 3-2: Examples of Version Ranges
Range |
Meaning |
(,1.0] |
Less
than or equal to 1.0 |
[1.2,1.3] |
Between
1.2 and 1.3 (inclusive) |
[1.0,2.0) |
Greater
than or equal to 1.0, but less than 2.0 |
[1.5,) |
Greater
than or equal to 1.5 |
(,1.1),(1.1,) |
Any
version, except 1.1 |
By being more specific through the use of version ranges, it is
possible to make the dependency mechanism more reliable for your builds
and to reduce the number of exception cases that will be required.
However, you need to avoid being overly specific as well. For instance,
if two version ranges in a dependency graph do not intersect at all, the
build will fail.
To understand how version ranges work, it is necessary to understand
how versions are compared. In figure 3-3, you can see how a version is
partitioned by Maven.
Figure 3-3: Version parsing
As you can see, a version is broken down into five parts: the major,
minor and bug fix releases, then the qualifier and finally a build
number. In the current version scheme, the snapshot (as shown above) is a
special case where the qualifier and build number are both allowed. In a
regular version, you can provide only the qualifier, or only the build
number. It is intended that the qualifier indicates a version prior to
release (for example, alpha-1, beta-1, rc1
). For a
qualifier to be a snapshot the qualifier must be the text “snapshot” or a
time stamp. The time stamp in figure 3-3 was generated on 11-02-2006 at
13:11:41. The build number is an increment after release to indicate
patched builds.
With regard to ordering, the elements are considered in sequence to
determine which is newer – first by major version, second – if the major
versions were equal – by minor version, third by bug fix version,
fourth by qualifier (using string comparison), and finally, by build
number. A version that contains a qualifier is older than a version
without a qualifier; for example, 1.2-beta is older than version 1.2. A
version that also contains a build number is considered newer than a
version without a build number; for example, 1.2-beta-1 is newer than
1.2-beta. In some cases, the versions will not match this syntax. In
those cases, the two versions are compared entirely as strings. Please
see the figure below for more examples of the ordering of version
parsing schemes.
Figure 3-4: Version Parsing
Note:
The use of version parsing
in Maven as defined here is considered the best practice. Based on
Maven’s version parsing rules you may also define your own version
practices.
All of these elements are considered part of the version and as such
the ranges do not differentiate. If you use the range [1.1,), and the
versions 1.1 and 1.2-beta-1 exist in a referenced repository, then
1.2-beta-1 will be selected. Often this is not desired, so to avoid such
a situation, you must structure your releases accordingly, either
avoiding the naming convention that would result in that behavior, or
through the use of a separate repository containing only the artifacts
and versions you strictly desire.
Whether you use snapshots until the final release, or release betas
as milestones along the way, you should deploy them to a snapshot
repository as is discussed in Chapter 7 of this book. This will ensure
that the beta versions are used in a range only if the project has
declared the snapshot (or development) repository explicitly.
A final note relates to how version updates are determined when a
range is in use. This mechanism is identical to that of the snapshots
that you learned in section 3.6. By default, the repository is checked
once a day for updates to the versions of artifacts in use. However,
this can be configured per-repository to be on a more regular interval,
or forced from the command line using the -U
option for a
particular Maven execution.
If it will be configured for a particular repository, the updatePolicy
value (which is in minutes) is changed for releases. For example:
<repository>
[...]
<releases>
<updatePolicy>interval:60</updatePolicy>
</releases>
</repository>
分享到:
相关推荐
Learn how to detect and fix dependency conflicts Learn how to share transitive relations and to visualize your dependencies In Detail Managing dependencies in a multi-module project is difficult. In a...
Learn how to use Gradle's powerful dependency management through extensive code samples, and discover how to define, customize, and deploy dependencies About This Book Be in total control of your ...
- **Troubleshooting**: Techniques for diagnosing and resolving common issues. #### Chapter 11: Managing Network Connections Network configuration and management are crucial in today's interconnected ...
Master the best practices of Git with the help of real-time scenarios to maximize team ... Finding and Resolving Conflicts Chapter 4. Going Deeper into Git Chapter 5. Using Git for Continuous Integration
Network applications must handle events intelligently and efficiently, establishing priorities, resolving conflicts, and managing resources to avoid blocks, dropouts, and the other jams that occur in ...
Network applications must handle events intelligently and efficiently, establishing priorities, resolving conflicts, and managing resources to avoid blocks, dropouts, and the other jams that occur in ...
1. Managerial skills and leadership are essential in resolving conflicts and building trust with employees. 2. Active listening and empathy are important skills for managers to resolve conflicts and ...
在ANSYS CFD软件中,尺度解析模拟(Scale-Resolving Simulations, SRS)是解决复杂流体动力学问题的重要工具,它能提供比传统雷诺平均 Navier-Stokes(RANS)模型更精确的流动预测。本文将深入探讨SRS模型的原理、...
Resolving Ethical Dilemmas: A Guide for Clinicians, Fifth Edition Now in its Fifth Edition, this respected reference helps readers tackle the common and often challenging ethical issues that affect ...
* Dual version of XmlEditorEx demo for D7 and DXE by use of {$ifdef}'s Version 3.12 (31dec2010) + Experimental unit NativeXmlC14n.pas (for canonicalization) ! Many bugfixes in NativeXmlEx parser + ...
### 解决增强现实中虚拟与真实场景间的遮挡问题 #### 引言 增强现实(Augmented Reality,简称AR)技术的目标是将计算机生成的信息添加到真实物体或场所中,与虚拟现实不同,它不是创造一个模拟的真实环境,而是以...