Originally from: https://zeroturnaround.com/rebellabs/java-build-tools-how-dependency-management-works-with-maven-gradle-and-ant-ivy/
can run at compile time, problem at runtime
In today's Java Project Automation building scenario, dependency Management has become a major part of the Project Building Automation tool, but it has always been the case in the past.
Looking back on the time when it was cool, your project dependency management only needs to import the jar package into the Lib folder and then add it to your version control system.
Or you can take a more adventurous approach, you can write a script that downloads the desired version of the library file from an external source, and also to the immortal bless the external URL does not change.
Doing all of this in a manual way will be a daunting and cumbersome process, especially given the transparent dependencies (dependent dependencies) that may occur only when the code of a library file is executed.
This can cause some difficulty in running the process, even if the compilation process can fix the problem will be found and resolved to have a transparent dependency.
Fortunately, this situation is no longer a major problem in today's project building automation tools (which is problematic).
The modularity of the project became more and more popular, and the need for internal projects and external dependencies increased, as a result all common project automation build tools strengthened and supported dependency management for this challenge, whether out of box or through plugins.
To make it easier for developers, they all use the same syntax to define dependencies, and they can also extract dependency relationships (such as Maven Central) from the same common repository (public artifact repositories).
But as convenient as these public repositories are, they also bring complexity to the project, conflict of transparent dependencies, and increased reliance on remote libraries.
How to define Dependencies
The most common syntax for defining dependencies is to add Group-id,artifact-id and required version numbers to the dependencies section of the build script. The project builder will attempt to resolve these dependencies, both in their local environment and in a remotely defined library.
In the following example, you will be shown how Google's public repository guava dependencies. Before you start, you need to know its group-id (Com.google.guava), its artifact-id (guava), and the version number of the library you are interested in,
(Take the recent version "15.0" for example). With this information in hand, you can add these dependencies to the dependency section of the build script.
The following are examples in Maven,gradle and Ant Ivy:
Maven
Gradle
Ant+ivy (Ivy.xml, or in Build.xml)
Now you can see how the previous parameters are used in these three examples, which may be a little cumbersome in maven.
The name of the property may be slightly different in the build tool, but it is generally similar.
How to use the version range
What if you don ' t want to depend on a specific version of a library, but know that any version in a specific range would do ? Or If you know this any version except ' X ' would work? This is the where version ranges can come in handy.
Instead of just a single version number, which can also specify a range, using a notation very similar to how do you would d Efine an interval in mathematics:parenthesis () work as exclusive markers and brackets [] as inclusive markers. Using a range allows the tool to pick a suitable version for the build. Continuing the example with guava, if you know, a version between 12.0 and up to 15.0 (excluded) would work, you cou LD define it as "[12.0,15.0)". You can also leave it open-ended like "[12.0,)", which adds a requirement for all version from 12.0 and up, similarly "(, 1 2.0] "would is any version up to and including 12.0. Or If you want anything higher than 12.0, except for version 13.0, you could specify "[12.0,13.0], (13.0,)" as the version.
But what use ranges at all? It's an easy and convenient-to-get a newer version of the library without has to change your build script; However, it also sets you up for potential trouble, should the author of the library opt-to-change functionality or the AP I that you ' re relying on! Another caveat about using ranges are that if the version numbering are inconsistent or doesn ' t follow some standard, things Might not go as expected. Using ranges on artifacts with qualifiers in the version string (like SNAPSHOT, ALPHA, BETA etc) also doesn ' t always go as Expected, as the range definition only supports numerical intervals and the build tool might pick a beta version because It has a higher number than the release version.
Besides ranges and specific versions, dependencies can also is resolved using dynamic versions by using the keywords ' L Atest ' or ' RELEASE ' instead of the version number. Using Those the build tool inquire the artifact repository about which version is the latest (or latest release) and use that version. The same caveats apply here as with version ranges Though–any changes to the API or functionality might break the world. Transitive dependencies and dependency conflicts
Let's go back on time to the "good old days" again. Here, you had full control and a overview over which libraries were used, since it is immediately visible which Librarie s were present in the Lib-folder. But with declarative dependencies remotely available, and the transitive dependencies that is automatically included as W ELL, this easy overview of which libraries is in use has become somewhat obscured. Luckily, most build tools has a plugin or an option to list the entire dependency tree:
- Maven:
mvn dependency:tree -Dverbose
- Gradle:
gradle -q dependencies
- Ivy:
<report conf="compile" />
Ivy ' s report option allows-generate the reports in various different formats, the default being an HTML and GRAPHML Report So is straight-forward as the console output you get from Maven and Gradle.
But what happens if there is conflicts in the dependencies? What if library A and library B both depend in library C, but require different versions of it? This is the where things start to get a bit tricky, and where build tools ' dependency management implementations diverge.
Assuming we have a project dependency structure The looks something like this:
- Project
- Module A
- COM.GOOGLE.GUAVA:GUAVA:[11.0,12.99]
- Module B
- com.google.guava:guava:[13.0,)
Trying to build the above project with Maven would result in a error because the dependencies could not being resolved, since No version of guava can satisfy both ranges. Equivalent Gradle build script and build it with Gradle, it'll pick the highest version of guava avail Able in either of the ranges; Which in this case means version ' 15.0 '. Changing the dependency of Module B, so it range is ' [12.0,] ', Maven would now pick version ' 12.0.1 ', which satisfies both Ranges Gradle still picks version ' 15.0 '.
Ivy and Gradle acts very similar in these scenarios, which isn ' t that surprising considering Gradle originally used Ivy as Their underlying dependency management implementation until they implemented their own dependency resolution engine.
The usage of ranges isn ' t that widely-used though, and the "more common" use case are to just has the simple version number Listed in the dependency. Even in this simple case, Maven, Gradle and Ivy again act vastly different when resolving dependencies!
Maven utilizes a "nearest definition" strategy, in which the closest version to the root was the version it ' ll use Througho UT the entire build! In the case of the structure above, module A and module B both depend on guava, and they is both found at the same depth; But since module A was listed in the project before Module B, the dependency for guava used there would be the version used For the entire build, even if the latter relies on a higher version!
Due to this, the a common approach for have better control over which version of conflicting dependencies is used was to add A dependency to the wanted version in the parent pom file. Since This was the first pom to be parsed, the dependencies listed there would always be nearest, thus Maven should use that Version and ignore every other version mentioned in the dependency graph.
As opposed to Maven, both Gradle and Ivy by default resolve dependency conflicts with a simple strategy:they just select The highest version:) If This strategy doesn ' t suit your needs, you can select a different strategy:for instance, force the build to fail Shoul D A dependency conflict arise, or forcing a specific version of the dependency to being used, overriding any version Otherwis e defined as part of the dependency graph. Final words
While the above is just a short introduction to some of the great and is great things about dependency management, th ESE is some of the things to keep in mind when dealing with it, and a good excuse to start reading up on the Documentatio N for your build tool to see exactly what's happening behind the screen.
- Maven: Introduction to the Dependency mechanism are a good place to Start:http://maven.apache.org/guides/introduc Tion/introduction-to-dependency-mechanism.html
Gradle: A good starting point is Chapter the its user Guide:http://www.gradle.org/docs/current/userguide/depen Dency_management.html
Ant + Ivy: A slightly obfuscated path to the documentation can be found here:http://ant.apache.org/ivy/history/la Test-milestone/ivyfile/dependency.html
Even though keeping binary dependencies in your VCS are somewhat frowned upon today, the idea of have complete control ov Er which libraries you include are still a good idea. No matter which build tools you prefer (or is required to use), it's always a good idea to know how they handle your Depe Ndencies; An old version of a library might introduce bugs, strangeness or, in the worst case, security risks to your production sys Tem! Keeping a handle on this can potentially save your a lot of headaches down the road.
Java build tools: How to use Maven,gradle and Ant+ivy for dependency management