Maven: optional dependencies and dependency exclusions

Introduction

This section discusses the functionality of optional dependencies and dependency exclusions. This will help users to understand what are they, how to use them, how they work and when is the best way to use them. It also explains why exclusions are made as per dependency basis not in a POM level.

Optional Dependencies

Optional dependencies are used when it's not really possible (for whatever reason) to split a project up into sub-modules. The idea is that some of the dependencies are only used for certain features in the project, and will not be needed if that feature isn't used. Ideally, such a feature would be split into a sub-module that depended on the core functionality project...this new subproject would have only non-optional dependencies, since you'd need them all if you decided to use the subproject's functionality.

However, since the project cannot be split up (again, for whatever reason), these dependencies are declared optional. If a user wants to use functionality related to an optional dependency, they will have to redeclare that optional dependency in their own project. This is not the most clear way to handle this situation, but then again both optional dependencies and dependency exclusions are stop-gap solutions.

Why use optional dependencies?

It's not only important to declare optional dependencies in order to save space/memory/etc. It's vital to control the list of actual dependencies a person needs in order to use a project, since these jars may eventually make it into a WAR, EAR, EJB, etc. Inclusion of the wrong jars may violate a license agreement, cause classpath issues, etc.

How do I use the optional tag?

A dependency is declared as optional by simply setting the <optional> tag to true in your dependency declaration. See the sample below:

<project>
 ...
 <dependencies>
 <!-- declare the dependency to be set as optional -->
 <dependency>
 <groupId>sample.ProjectA</groupId>
 <artifactId>Project-A</artifactId>
 <version>1.0</version>
 <scope>compile</scope>
 <optional>true</optional> <!-- value will be true or false only -->
 </dependency>
 </dependencies>
</project>

<a name="How_do_optional_dependencies_work"></a>How do optional dependencies work?

Project-A -> Project-B

The diagram above says that Project-A depends on Project-B. When A declares B as an optional dependency in its POM, this relationship remains unchanged. Its just like a normal build where Project-B will be added in its classpath.

Project-X -> Project-A

But when another project(Project-X) declares Project-A as a dependency in its POM, the optional dependency takes effect. You'll notice that Project-B is not included in the classpath of Project-X; you will need to declare it directly in your POM in order for B to be included in X's classpath.

Example

Let us say that there is a project named X2 that has similar functions with Hibernate which supports many database drivers/dependencies such as mysql, postgre, oracle etc. All of these dependencies are needed for X2 to build but not for your project, so it is very practical for X2 to declare these dependencies as optional, so that whenever your project declares X2 as a direct dependency in your POM, all the drivers supported by the X2 will not be automatically included to your project's classpath instead you'll have to declare it directly on what driver/dependency of the database you are going to use.

Dependency Exclusions

Since maven 2.x resolves dependencies transitively, it is possible for unwanted dependencies to be included in your project's classpath. Projects that you depend on may not have declared their set of dependencies correctly, for example. In order to address this special situtation, maven 2.x has incorporated the notion of explicit dependency exclusion. Exclusions are set on a specific dependency in your POM, and are targeted at a specific groupId and artifactId. When you build your project, that artifact will not be added to your project's classpath by way of the dependency in which the exclusion was declared.

How to use dependency exclusions

We add the <exclusions> tag under the <dependency> section of the pom.

<project>
  ...
  <dependencies>
    <dependency>
      <groupId>sample.ProjectA</groupId>
      <artifactId>Project-A</artifactId> 
      <version>1.0</version>
      <scope>compile</scope>
      <exclusions>
        <exclusion> <!-- declare the exclusion here -->
          <groupId>sample.ProjectB</groupId>
          <artifactId>Project-B</artifactId>
        </exclusion>
      </exclusions> 
    </dependency>
  </dependencies>
</project>

How dependency exclusion works and when to use it ( as a last resort! )

Project-A
  -> Project-B
    -> Project-D <! -- This dependency should be excluded -->
      -> Project-E
      -> Project-F
    -> Project C

The diagram shows that Project-A depends on both Project-B and C. Project-B depends on Project-D. Project-D depends on both Project-E and F. By default, Project A's classpath will include:

B, C, D, E, F

What if we dont want project D and its dependencies to be added to Project A's classpath because we know some of Project-D's dependencies (maybe Project-E for example) was missing from the repository, and you don't need/want the functionality in Project-B that depends on Project-D anyway. In this case, Project-B's developers could provide a dependency on Project-D that is <optional>true</optional>, like this:

<dependency>
  <groupId>sample.ProjectD</groupId>
  <artifactId>ProjectD</artifactId>
  <version>1.0-SNAPSHOT</version>
  <optional>true</optional>
</dependency>

HOWEVER, they didn't. As a last resort, you still have the option to exclude it on your side, in Project-A, like this:


<project>
   <modelVersion>4.0.0</modelVersion>
   <groupId>sample.ProjectA</groupId>
   <artifactId>Project-A</artifactId>
   <version>1.0-SNAPSHOT</version>
   <packaging>jar</packaging>
 ...
   <dependencies>
     <dependency>
       <groupId>sample.ProjectB</groupId>
       <artifactId>Project-B</artifactId>
       <version>1.0-SNAPSHOT</version>
       <exclusions>
         <exclusion>
           <groupId>sample.ProjectD</groupId> <!-- Exclude Project-D from Project-B -->
           <artifactId>Project-D</artifactId>
         </exclusion>
       </exclusions>
     </dependency>
   </dependencies>
 </project>

If we deploy the Project-A to a repository, and Project-X declares a normal dependency on Project-A, will Project-D be excluded from the classpath still?

Project-X -> Project-A

The answer is Yes. Project-A has declared that it doesn't need Project-D to run, so it won't be brought in as a transitive dependency of Project-A.

Now, consider that Project-X depends on Project-Y, as in the diagram below:

Project-X -> Project-Y
 -> Project-B
 -> Project-D
 ...

Project-Y also has a dependency on Project-B, and it does need the features supported by Project-D. Therefore, it will NOT place an exclusion on Project-D in its dependency list. It may also supply an additional repository, from which we can resolve Project-E. In this case, it's important that Project-D is not excluded globally, since it is a legitimate dependency of Project-Y.

As another scenario, what if the dependency we don't want is Project-E instead of Project-D. How will we exclude it? See the diagram below:

Project-A
  -> Project-B
    -> Project-D 
      -> Project-E <!-- Exclude this dependency -->
      -> Project-F
  -> Project C

Exclusions work on the entire dependency graph below the point where they are declared. If you wanted to exclude Project-E instead of Project-D, you'd simply change the exclusion to point at Project-E, but you wouldn't move the exclusion down to Project-D...you cannot change Project-D's POM. If you could, you would use optional dependencies instead of exclusions, or split Project-D up into multiple subprojects, each with nothing but normal dependencies.

<project>
 <modelVersion>4.0.0</modelVersion>
 <groupId>sample.ProjectA</groupId>
 <artifactId>Project-A</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>
 ...
 <dependencies>
 <dependency>
 <groupId>sample.ProjectB</groupId>
 <artifactId>Project-B</artifactId>
 <version>1.0-SNAPSHOT</version>
 <exclusions>
 <exclusion>
 <groupId>sample.ProjectE</groupId> <!-- Exclude Project-E from Project-B -->
 <artifactId>Project-E</artifactId>
 </exclusion>
 </exclusions>
 </dependency>
 </dependencies>
</project>

Why exclusions are made on a per-dependency basis, rather than at the POM level

This is mainly done to be sure the dependency graph is predictable, and to keep inheritance effects from excluding a dependency that should not be excluded. If you get to the method of last resort and have to put in an exclusion, you should be absolutely certain which of your dependencies is bringing in that unwanted transitive dependency.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容