Maven - Dealing with dependencies

Maven dependencies feature is maybe one of the biggest shift that your project will have. But the good news is that you only need to do it once. After mapping and writing to the POM file every JAR that your project need may be easy to change and maintain any library dependency.

At this time, we start a kind of Maven configuration cycle. The same way as you defined your POM file, other projects defined theirs. So the first task is to search for the groupId, artifactId, and version of your dependency.

Dependencies

The default place to find it, is at the Mvn Repository. It’s easy, just type the library on the search bar to find almost anything you want.

Let’s take a very common library by example: Apache Commons Lang. It’s an awesome utility project that many softwares use.

Searching for commons lang

That’s it! We find the groupId org.apache.commons and the artifactId commons-lang3. Clicking on commons-lang3 link you’ll be redirected to all available versions. Select the version used by your project and copy the snippet code to your POM file.

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.6</version>
</dependency>

Remember that all dependency tags are embraced by a parent tag dependencies.

<dependencies>
    <!-- . . . -->
</dependencies>

The common process would be to repeat this step with every project dependency to get rid of all loose libraries, almost that. I will tell you why.

Transitive Dependencies

Maven has a great feature known as Transitive Dependencies. It means that many of your dependencies aren’t used directly from your app. Sometimes it’s a library that a specific dependency needs.

Let’s put an example to make things clear. Check the spring-webmvc dependency at MVN Repository.

On a non-Maven project, you would have to import all required compile dependencies, about 7 in this case, to configure your libraries. I can’t believe that I did it so many times. It’s very frustrating when we need to change the framework version and check all dependencies again.

The good news is that probably you only need to check the main dependencies of your project like a Spring or Struts. Then check all other libraries from your project verifying if they’re necessary to compile this framework. In this example, you would transform 7 JAR files into one single dependency.

Now Maven takes care of resolving possible dependencies conflicts (generally it chooses the newer version from conflict) and downloading all libraries. No more frustration to change a framework version. In this case, the Spring guy has already checked all dependency versions for you.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.0.0.RELEASE</version>
</dependency>

Don’t forget other main libraries like Hibernate, a specific log framework, and so on. They could also have some compile dependencies.

Take a look at this simple demo using Spring and Hibernate dependencies. Execute mvn dependency:tree on the root folder, where POM file is located.

$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Archetype - maven-demo 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ maven-demo ---
[INFO] tech.cyborgdeveloper:maven-demo:jar:1.0-SNAPSHOT
[INFO] +- org.springframework:spring-webmvc:jar:5.0.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-aop:jar:5.0.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-beans:jar:5.0.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-context:jar:5.0.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-core:jar:5.0.0.RELEASE:compile
[INFO] |  |  \- org.springframework:spring-jcl:jar:5.0.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-expression:jar:5.0.0.RELEASE:compile
[INFO] |  \- org.springframework:spring-web:jar:5.0.0.RELEASE:compile
[INFO] \- org.hibernate:hibernate-core:jar:5.2.11.Final:compile
[INFO]    +- org.jboss.logging:jboss-logging:jar:3.3.0.Final:compile
[INFO]    +- org.hibernate.javax.persistence:hibernate-jpa-2.1-api:jar:1.0.0.Final:compile
[INFO]    +- org.javassist:javassist:jar:3.20.0-GA:compile
[INFO]    +- antlr:antlr:jar:2.7.7:compile
[INFO]    +- org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:jar:1.0.1.Final:compile
[INFO]    +- org.jboss:jandex:jar:2.0.3.Final:compile
[INFO]    +- com.fasterxml:classmate:jar:1.3.0:compile
[INFO]    +- dom4j:dom4j:jar:1.6.1:compile
[INFO]    \- org.hibernate.common:hibernate-commons-annotations:jar:5.0.1.Final:compile
[INFO] ------------------------------------------------------------------------
...

Here are extra tips, because sometimes we only need a simple help to avoid reinventing the wheel.

  • mvn dependency:tree -Dverbose will show all omitted and conflicted dependencies.
  • mvn dependency:help let you discover all available goals to this plugin.
  • mvn dependency:help -Ddetail=true -Dgoal=<goal-name> may help you to search for advanced parameters to a specific goal as tree.

Versions

The version is a diverse topic. Each company deals within a particular way. But there are some conventions and I would suggest a reading: Semantic Versioning. This site may introduce you to some concepts about version numbers if you aren’t familiar yet.

All these numbers sometimes are followed by a word. Let’s cover some of them:

  • SNAPSHOT: A good one to avoid many version changes during development. You must never use this version in production and has to be all written in capital letters.
  • M1 (Milestone Release) or RC1 (Release Candidate): Close to reach a stable level, to be ready for a secure utilization. But maybe still have some bugs.
  • RELEASE or Final: Stable versions ready to production.

Spring follow this approach to upload only stable versions to Maven Repository. But they have a specific repository to provide SNAPSHOT versions. Check a spring-webmvc example.

Scopes

In a simple way, scopes define if a dependency will be available on the project classpath, or expected to be in other location, or will be used only in a specific moment. Let’s summarize:

  • compile: default scope that makes the library available on project classpath.
  • provided: like the compile, available on all software life cycle, but won’t be packaged together with the project. You expect that your container provides it at runtime. A great example is servlet-api. You need it during the development but Tomcat will provide it for you at runtime.
  • runtime: not required for compilation but for execution like dynamic libraries or JDBC drivers.
  • test: available only to compile and execute tests.
  • system: similar to provide except that you have to hardcode the path to the JAR file. Try to not use this scope due to its weakness to maintain.
  • import: advanced topic dealing with dependencyManagement to share resources across multiple POMs.

Next Step

On the last article, we understand the folder structure. Now we learn how to configure our project dependencies. Next step is the big picture of Maven life cycle.

Comment below about your challenges during this process. Let me help you on any issue you have. Ping me on twitter or by email (rcmoutinho@cyborgdeveloper.tech) anytime you want.

Let’s automate!


Leave a Reply

Your email address will not be published.