在Mac OS X + Idea下搭建Maven项目 -- Maven的依赖管理

上一篇博文讲到如何安装maven,这篇算是它的续篇,总结下maven是如何管理依赖的。

maven强大之处就是管理依赖。通过依赖传递,依赖继承,以及pom导入,可以轻松实现依赖的版本管理。通过依赖scope实现在不同的生命周期时段,加入依赖。

如下是一个最简单的为当前项目添加依赖的示例:
IntelliJ Idea > Preferences… > Build, Execution, Deployment > Build Tools > Maven
Importing一栏,将”Import Maven projects automatically” 勾选上。如果这一栏没有勾选的话,Idea会在右上角有个漂浮提示,也可以从这个提示那里点击开启。
接下来,往pom.xml里添加一条依赖,IDE会自动将此依赖添加到External Libraries里面。

     <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

#### 依赖传递 ####
比如我在pom.xml里加上了junit. 而junit要依赖于hamcrest-core。但是我不用再添加对于hamcrest-core的依赖,maven会自动做掉,于是我的External Libraries里面不仅有junit,还有hamcrest-core.

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

#### 依赖scope ####

  1. compile: 默认的scope,如果在dependency节点里没有写scope,那么它默认是compile
  2. provided: JDK/容器在运行时会产生/提供这个依赖
  3. runtime: 依赖仅在运行时需要,在编译时是不需要的
  4. test: 依赖仅用于测试,并不用于真正的appliation,比如添加junit依赖时,就可以为它加上scope = test,前提是application本身真的不需要用到junit
  5. system: 貌似已经弃用了…
  6. import: 导入别的pom的dependencyManagement部分

#### 依赖继承 ####
依赖继承有两个重要作用:

  1. 将子项目的公用依赖加入到父项目里,化简子项目的pom.xml
  2. 通过父项目的dependencyManagement里申明的版本号来管理所用依赖的版本

如下例子来自maven官方文档。Project A是父项目。Project B是A的子项目,所以B继承了A申明在dependencyManagement段落里的依赖,即:a-1.2,b-1.0,c-1.0,d-1.2。

但是由于B自己本身有定义对于a和c的依赖,所以对于依赖a, B会用自己本身定义的a-1.0。对于依赖c,B里面并未定义版本,所以maven会上溯至父亲A中找到对c的版本定义,所以是c-1.0。对于依赖b,它没有定义在B自己的pom里,所以继承自父亲A,b-1.0。对于依赖d,父亲A里有,自己的dependencyManagement也有,优先选用自己的,所以是c-1.0。

  <parent>
    <artifactId>A</artifactId>
    <groupId>maven</groupId>
    <version>1.0</version>
  </parent>

Project A的pom.xml:

<project>
 <modelVersion>4.0.0</modelVersion>
 <groupId>maven</groupId>
 <artifactId>A</artifactId>
 <packaging>pom</packaging>
 <name>A</name>
 <version>1.0</version>
 <dependencyManagement>
   <dependencies>
     <dependency>
       <groupId>test</groupId>
       <artifactId>a</artifactId>
       <version>1.2</version>
     </dependency>
     <dependency>
       <groupId>test</groupId>
       <artifactId>b</artifactId>
       <version>1.0</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>test</groupId>
       <artifactId>c</artifactId>
       <version>1.0</version>
       <scope>compile</scope>
     </dependency>
     <dependency>
       <groupId>test</groupId>
       <artifactId>d</artifactId>
       <version>1.2</version>
     </dependency>
   </dependencies>
 </dependencyManagement>
</project>

Project B的pom.xml:

<project>
  <parent>
    <artifactId>A</artifactId>
    <groupId>maven</groupId>
    <version>1.0</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <groupId>maven</groupId>
  <artifactId>B</artifactId>
  <packaging>pom</packaging>
  <name>B</name>
  <version>1.0</version>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>test</groupId>
        <artifactId>d</artifactId>
        <version>1.0</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>test</groupId>
      <artifactId>a</artifactId>
      <version>1.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>test</groupId>
      <artifactId>c</artifactId>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
</project>

一般来说,将dependencyManagement置于顶层项目的pom.xml文档中,用于依赖包的版本控制,以确保各个子项目中用到的依赖都是相同的版本。dependencyManagement仅申明依赖和版本,并不实际下载依赖包。

当子项目中的dependencies里有申明某依赖且没有版本号时,maven会上溯,直到在上层pom里找到该依赖的版本。子项目不用显示地写依赖的版本,以达到统一管理依赖版本的目的。

如果想要将依赖传递下去(传给子项目,或者传给那些import 这个pom的项目),则把依赖定义在dependencyManagement部分。如果不想用父亲/import进来的pom里定义的依赖版本,则在dependency部分申明特定版本号的依赖。否则,就应该在dependency部分仅申明依赖,不申明依赖版本,统一用父亲/import进来的pom里定义的依赖版本,来实现版本管理。

#### 依赖导入 ####
在大型项目中,依赖导入使用的更多。这里用依赖导入来实现刚刚在依赖继承小节实现的B继承A的目的。
在B的pom.xml的dependencyManagement部分,import A的pom:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>maven</groupId>
  <artifactId>B</artifactId>
  <packaging>pom</packaging>
  <name>B</name>
  <version>1.0</version>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>maven</groupId>
        <artifactId>A</artifactId>
        <version>1.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>test</groupId>
        <artifactId>d</artifactId>
        <version>1.0</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>test</groupId>
      <artifactId>a</artifactId>
      <version>1.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>test</groupId>
      <artifactId>c</artifactId>
      <scope>runtime</scope>
    </dependency>
  </dependencies>
</project>

在大型项目中,某些模块依赖某些项目是非常常见的,为了使这些模块的依赖项目版本统一,使用依赖继承+依赖导入是一种非常好的实践。在根pom中指明依赖版本,在模块中导入根pom,统一使用根pom定义的版本。将来依赖版本有更新时,只用更改根pom里的版本号即可。这一点颇有设计模式的味道…

首先在根pom–bom(bill of material)的dependencyManagement部分申明依赖project1和project2,版本的话则引用properties部分定义的变量。并且根pom里指明子项目parent。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>bom</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>
  <properties>
    <project1Version>1.0.0</project1Version>
    <project2Version>1.0.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>${project1Version}</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <modules>
    <module>parent</module>
  </modules>
</project>

接下来在parent项目的pom-dependencyManagement部分,定义了一些外部依赖,并指定了版本。而且还定义了子项目project1和project2.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.test</groupId>
    <version>1.0.0</version>
    <artifactId>bom</artifactId>
  </parent>

  <groupId>com.test</groupId>
  <artifactId>parent</artifactId>
  <version>1.0.0</version>
  <packaging>pom</packaging>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
      </dependency>
      <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.1</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <modules>
    <module>project1</module>
    <module>project2</module>
  </modules>
</project>

接下来是project1和project2的pom,它们的pom里只申明了dependency而并未申明版本,因为要统一用父亲parent pom里定义的依赖版本。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.test</groupId>
    <version>1.0.0</version>
    <artifactId>parent</artifactId>
  </parent>
  <groupId>com.test</groupId>
  <artifactId>project1</artifactId>
  <version>${project1Version}</version>
  <packaging>jar</packaging>

  <dependencies>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
    </dependency>
  </dependencies>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.test</groupId>
    <version>1.0.0</version>
    <artifactId>parent</artifactId>
  </parent>
  <groupId>com.test</groupId>
  <artifactId>project2</artifactId>
  <version>${project2Version}</version>
  <packaging>jar</packaging>

  <dependencies>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
    </dependency>
  </dependencies>
</project>

最后,当别的项目要引用project1和project2时,直接在dependencyManagement部分导入根pom(即bom),然后在dependencies部分申明要用到的依赖。不用指明版本,因为maven会取根pom(即bom)里在properties部分定义的版本。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>use</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.test</groupId>
        <artifactId>bom</artifactId>
        <version>1.0.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>

maven官方文档链接:
https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章