Maven学习笔记

时间:2022-09-25 11:56:17

1.Maven安装

Maven和ant同为apache出版的构建工具,与gradle是一类东西,与C语言中的make是同一类产品.从apache官网上下载maven的zip安装包,解压即可使用,需要把解压后的bin目录添加到环境变量中去.
Eclipse是自带maven的,如果不想使用eclipse的内置maven,可以在preference->maven->installations中进行设置.

2.最简的pom.xml文件

pom的意思是project object model项目对象模型

<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.jenkov</groupId>
    <artifactId>java-web-crawler</artifactId>
    <version>1.0.0</version>
</project>

其中,modelVersion表示正在使用的pom模型版本,groupId表示项目名称,artifactId表示模块名称,一个项目可以包含多个模块.

3.pom的继承

一个pom可以继承自父pom,它可以继承父pom的dependencies,要想让一个pom继承另一个pom,可以使用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>org.codehaus.mojo</groupId>
        <artifactId>my-parent</artifactId>
        <version>2.0</version>
        <relativePath>../my-parent</relativePath>
    </parent>
    <artifactId>my-project</artifactId>
    ...
</project>

子pom中的值会覆盖父pom中的值.因为pom的继承关系,最终的pom成为有效pom,可以通过mvn help:effective-pom命令来查看最终的pom.
每一个pom.xml默认继承super pom,super pom内容如下:

<project>
  <modelVersion>4.0.0</modelVersion>
  <name>Maven Default Project</name>

  <repositories>
    <repository>
      <id>central</id>
      <name>Maven Repository Switchboard</name>
      <layout>default</layout>
      <url>http://repo1.maven.org/maven2</url>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
    </repository>
  </repositories>

  <pluginRepositories>
    <pluginRepository>
      <id>central</id>
      <name>Maven Plugin Repository</name>
      <url>http://repo1.maven.org/maven2</url>
      <layout>default</layout>
      <snapshots>
        <enabled>false</enabled>
      </snapshots>
      <releases>
        <updatePolicy>never</updatePolicy>
      </releases>
    </pluginRepository>
  </pluginRepositories>

  <build>
    <directory>target</directory>
    <outputDirectory>target/classes</outputDirectory>
    <finalName>${artifactId}-${version}</finalName>
    <testOutputDirectory>target/test-classes</testOutputDirectory>
    <sourceDirectory>src/main/java</sourceDirectory>
    <scriptSourceDirectory>src/main/scripts</scriptSourceDirectory>
    <testSourceDirectory>src/test/java</testSourceDirectory>
    <resources>
      <resource>
        <directory>src/main/resources</directory>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>src/test/resources</directory>
      </testResource>
    </testResources>
  </build>

  <reporting>
    <outputDirectory>target/site</outputDirectory>
  </reporting>

  <profiles>
    <profile>
      <id>release-profile</id>

      <activation>
        <property>
          <name>performRelease</name>
        </property>
      </activation>

      <build>
        <plugins>
          <plugin>
            <inherited>true</inherited>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-source-plugin</artifactId>

            <executions>
              <execution>
                <id>attach-sources</id>
                <goals>
                  <goal>jar</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <inherited>true</inherited>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-javadoc-plugin</artifactId>

            <executions>
              <execution>
                <id>attach-javadocs</id>
                <goals>
                  <goal>jar</goal>
                </goals>
              </execution>
            </executions>
          </plugin>
          <plugin>
            <inherited>true</inherited>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-deploy-plugin</artifactId>

            <configuration>
              <updateReleaseInfo>true</updateReleaseInfo>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>

</project>

4.maven的配置

maven配置文件中的配置相当于全局配置,对于所有的pom.xml都是有效的.在配置文件settings.xml中可以配置:

  • 本地仓库的路径,默认为~/.m2
  • 当前编译选项
  • 其他一些配置
    settings.xml有两个:一个是%Maven_Home%/conf/settings.xml,另一个是~/.m2/settings.xml.后者会覆盖前者的设置.

5.运行maven

maven构建项目有三个概念:生命周期,阶段,目标.maven内置三个生命周期,一个生命周期包括多个阶段,一个阶段包括多个目标.
运行maven只需要输入mvn命令,maven就会自动查找当前目录下的pom.xml并按照要求进行执行.
mvn命令还可以带一些参数,参数包括阶段和目标.
例如install就是一个阶段,它是在target目录中生成jar文件并将jar文件复制到maven本地仓库中去(即~/.m2目录中).执行install阶段就会默认把install之前的阶段全部执行,命令为mvn install
可以向mvn传递多个参数,表示执行多个阶段,例如mvn clean install,先进行clean把target中生成的jar删除,然后再执行install.
可以让mvn执行某个目标,命令形式为mvn 阶段名:目标名,例如mvn dependencies:copy-dependencies

6.maven项目的目录结构

maven遵循”约定大于配置”原则,对目录有默认的组织,如果不按照默认的来,就需要指明src目录,target目录等.

- src
  - main
    - java
    - resources
    - webapp
  - test
    - java
    - resources

- target

target目录是maven创建的,执行mvn clean命令之后target目录被清空.

7.项目依赖

项目A依赖项目B,项目B依赖项目C.众多的项目形成一个有向无环图(DAG),每一个项目相当于一个结点,每一条有向边表示一个依赖关系.如果要使用B.jar,不下载C.jar肯定会报错.使用maven,就可以自动推导出项目所依赖的全部jar包,解决项目依赖问题是maven的重要作用之一.maven会自动下载所需jar包到本地仓库中,在pom.xml中进行如下配置

<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.jenkov.crawler</groupId>
    <artifactId>java-web-crawler</artifactId>
    <version>1.0.0</version>

    <dependencies>
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.7.1</version>
        </dependency>

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

    </dependencies>

    <build>
    </build>
</project>

有的时候,指定的依赖在Maven的*仓库里没有。你可以直接下载这些依赖,然后放到Maven的本地仓库。这些依赖必须放到与groupId、 artifactId和version匹配的子目录中。用/替换所有的点(.)并且使用/分隔groupId、artifactId和version,这就是与该依赖匹配的子目录。
上面示例中的两个依赖将会被放到以下子目录中:
MAVEN_REPOSITORY_ROOT/junit/junit/4.8.1
MAVEN_REPOSITORY_ROOT/org/jsoup/jsoup/1.7.1
Maven仓库分为三种:本地仓库(即~/.m2),远程仓库(自己建立的maven服务器),中心仓库(http://repo1.maven.org/maven2).

8.外部依赖

有时只有一个jar包而没有它的groupId和artifactId,并且三种maven仓库中都没有这个jar包项目,但当前项目依赖这个jar包,这种依赖叫做外部依赖,可以用如下配置指定外部依赖.

<dependency>
  <groupId>mydependency</groupId>
  <artifactId>mydependency</artifactId>
  <scope>system</scope>
  <version>1.0</version>
  <systemPath>${basedir}\war\WEB-INF\lib\mydependency.jar</systemPath>
</dependency>

这种方式需要在每一个maven工程的pom.xml中这么写,这样岂不是十分麻烦.一种解决方案是把第三方jar包安装到本地maven仓库中去.
mvn install:install-file -Dfile= -DgroupId= -DartifactId= -Dversion= -Dpackaging=
在这个命令中,需要手动提供groupId,artifactId等,由此也可见一个pom所应包含的信息,groupId是作者信息,artifactId是作品名称,version是作品版本号,packaging是打包类型,可以取值war,jar等.如果第三方jar包有相应的pom.xml,则不必提供groupId,artifactId等信息,因为它们都在pom.xml里面写着呢.可以使用如下命令:
mvn install:install-file -Dfile= -DpomFile=

9.快照依赖

快照依赖指的是那些还在开发中的依赖(jar包)。与其经常地更新版本号来获取最新版本,不如你直接依赖项目的快照版本。快照版本的每一个build版本都会被下载到本地仓库,即使该快照版本已经在本地仓库了。总是下载快照依赖可以确保本地仓库中的每一个build版本都是最新的。
在pom文件的最开头(设置groupId和artifactId的地方),在版本号后追加-SNAPSHOT,则告诉Maven你的项目是一个快照版本。如:
<version>1.0-SNAPSHOT</version>
可以看到加到版本号后的-SNAPSHOT。
在配置依赖时,在版本号后追加-SNAPSHOT表明依赖的是一个快照版本。如:

<dependency>
    <groupId>com.jenkov</groupId>
    <artifactId>java-web-crawler</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

追加在version后的-SNAPSHOT告诉Maven这是一个快照版本.
可以在Maven配置文件中设置快照版本下载的频率.

10.maven仓库

Maven仓库就是存储jar包和一些元数据信息的目录。其中的元数据即pom文件,描述了该jar包属于哪个项目,以及jar包所需的外部依赖。该元数据信息使得Maven可以递归地下载所有的依赖,直到整个依赖树都下载完毕并放到你的本地仓库中。查找依赖时,首先是本地仓库,然后是*仓库,最后,如果pom文件中配置了远程仓库,则会去远程仓库中查找。
(1)本地仓库
本地仓库默认路径为~/.m2,可以在%MAVEN_HOME%/conf/settings.xml中设置本地仓库路径

<settings>
  <localRepository>
    d:\data\java\products\maven\repository
  </localRepository>
</settings>

可以通过mvn install命令构建自己的项目并安装到本地仓库中去.
(2)*仓库
Maven的*仓库由Maven社区提供。默认情况下,所有不在本地仓库中的依赖都会去这个*仓库查找。然后Maven会将这些依赖下载到你的本地仓库。访问*仓库不需要做额外的配置。
(3)远程仓库
远程仓库是位于web服务器上的一个仓库,Maven可以从该仓库下载依赖,就像从*仓库下载依赖一样。远程仓库可以位于Internet上的任何地方,也可以是位于本地网络中。
远程仓库一般用于放置组织内部的项目,该项目由多个项目共享。比如,由多个内部项目共用的安全项目。该安全项目不能被外部访问,因此不能放在公开的*仓库下,而应该放到内部的远程仓库中。
远程仓库中的依赖也会被Maven下载到本地仓库中。
可以在pom文件里配置远程仓库。将以下的xml片段放到属性之后:

<repositories>
  <repository>
    <id>jenkov.code</id>
    <url>http://maven.jenkov.com/maven2/lib</url>
  </repository>
</repositories>

11. Maven插件

使用Maven插件,可以向构建过程添加自定义的动作。创建一个简单的Java类,该类继承一个特殊的Maven类,然后为项目创建一个pom文件。该插件应该位于其项目下。

12.maven构建生命周期,阶段,目标

(1)生命周期
Maven有三个内嵌的生命周期:

  • default
  • clean
  • site

这三个生命周期是互不干扰的,default是默认生命周期,负责项目的编译和打包;clean是删除target目录中之前生成的.class和.jar等文件,site关注的是为项目生成文档,实际上site可以为项目生成一个网页式的文档.
一个生命周期包含多个构建阶段,每个构建阶段又包括一系列目标.
(2)构建阶段
每一个构建生命期被分为一系列的构建阶段,构建阶段又被分为构建目标。因此,整个构建过程由一系列的构建生命期、构建阶段和构建目标组成。
你可以执行一个构建生命期,如clean或site,一个构建阶段,如default生命期的install,或者一个构建目标,如dependency:copy-dependencies。注意:你不能直接执行default生命期,你需要指定default生命期中的一个构建阶段或者构建目标。
当你执行一个构建阶段时,所有在该构建阶段之前的构建阶段(根据标准构建顺序)都会被执行。因此,执行install阶段,意味着所有位于install阶段前的构建阶段都会被执行,然后才执行install阶段。
default生命期更多的关注于构建代码。由于你不能直接执行default生命期,你需要执行其中一个构建阶段或者构建目标。default生命期包含了相当多的构建阶段和目标,这里不会所有都介绍。最常用的构建阶段有:

  • validate 验证项目的正确性,以及所有必需的信息都是否都存在。同时也会确认项目的依赖是否都下载完毕。
  • compile 编译项目的源代码
  • test 选择合适的单元测试框架,对编译后的源码执行测试;这些测试不需要代码被打包或者部署。
  • package 将编译后的代码以可分配的形式打包,如Jar包。
  • install 将项目打包后安装到本地仓库,可以作为其它项目的本地依赖。
  • deploy 将最终的包复制到远程仓库,与其它开发者和项目共享。

将构建阶段的名称作为参数传给mvn命令,就是执行该构建阶段,如:mvn package
该示例执行package构建阶段,因此在Maven的构建阶段序列中所有位于该阶段之前的阶段也都会被执行。
如果标准的Maven构建阶段和目标无法满足项目的需要,可以创建Maven插件增加你需要的构建功能。
(3)构建目标
构建目标是Maven构建过程中最细化的步骤。一个目标可以与一个或多个构建阶段绑定,也可以不绑定。如果一个目标没有与任何构建阶段绑定,你只能将该目标的名称作为参数传递给mvn命令来执行它。如果一个目标绑定到多个构建阶段,该目标在绑定的构建阶段执行的同时被执行.

13. 传递性依赖

传递性依赖是Maven2.0的新特性。假设你的项目依赖于一个库,而这个库又依赖于其他库。你不必自己去找出所有这些依赖,你只需要加上你直接依赖的库,Maven会隐式的把这些库间接依赖的库也加入到你的项目中。这个特性是靠解析从远程仓库中获取的依赖库的项目文件实现的。一般的,这些项目的所有依赖都会加入到项目中,或者从父项目继承,或者通过传递性依赖。
传递性依赖的嵌套深度没有任何限制,只是在出现循环依赖时会报错。
传递性依赖会导致包含库的依赖图增长的非常大。为了解决这个问题,Maven也提供了额外的机制,能让你指定哪些依赖会被包含:
依赖调解 – 当项目中出现多个版本构件依赖的情形,依赖调解决定最终应该使用哪个版本。目前,Maven 2.0只支持“短路径优先”原则,意思是项目会选择依赖关系树中路径最短的版本作为依赖。当然,你也可以在项目POM文件中显式指定使用哪个版本。值得注意的是,在Maven2.0.8及之前的版本中,当两个版本的依赖路径长度一致时,哪个依赖会被使用是不确定的。不过从Maven 2.0.9开始,POM中依赖声明的顺序决定了哪个版本会被使用,也叫作”第一声明原则”。
“短路径优先”意味着项目依赖关系树中路径最短的版本会被使用。例如,假设A、B、C之间的依赖关系是A->B->C->D(2.0)和A->E->(D1.0),那么D(1.0)会被使用,因为A通过E到D的路径更短。但如果你想要强制使用D(2.0),那你也可以在A中显式声明对D(2.0)的依赖。
依赖管理 – 在出现传递性依赖或者没有指定版本时,项目作者可以通过依赖管理直接指定模块版本。之前的章节说过,由于传递性依赖,尽管某个依赖没有被A直接指定,但也会被引入。相反的,A也可以将D加入元素中,并在D可能被引用时决定D的版本号。
依赖范围 – 你可以指定只在当前编译范围内包含合适的依赖。
排除依赖 – 如果项目X依赖于项目Y,项目Y又依赖项目Z,项目X的所有者可以使用”exclusion”元素来显式排除项目Z。
可选依赖 – 如果项目Y依赖项目Z,项目Y的所有者可以使用”optional”元素来指定项目Z作为X的可选依赖。那么当项目X依赖项目Y时,X只依赖Y并不依赖Y的可选依赖Z。项目X的所有者也可以根据自己的意愿显式指定X对Z的依赖。(你可以把可选依赖理解为默认排除)。
下面对依赖范围进行详细说明.
依赖范围会影响传递性依赖,同时也会影响项目构建任务中使用的classpath。

Maven有以下6种依赖范围:

  • compile
    这是默认范围。如果没有指定,就会使用该依赖范围。编译依赖对项目所有的classpath都可用。此外,编译依赖会传递到依赖的项目。
  • provided
    和compile范围很类似,但provided范围表明你希望由JDK或者某个容器提供运行时依赖。例如,当使用Java EE构建一个web应用时,你会设置对Servlet API和相关的Java EE APIs的依赖范围为provided,因为web容器提供了运行时的依赖。provided依赖只对编译和测试classpath有效,并且不能传递。
  • runtime
    runtime范围表明编译时不需要依赖,而只在运行时依赖。此依赖范围对运行和测试classpath有效,对编译classpath无效。
  • test
    test范围表明使用此依赖范围的依赖,只在编译测试代码和运行测试的时候需要,应用的正常运行不需要此类依赖。
  • system
    系统范围与provided类似,不过你必须显式指定一个本地系统路径的JAR,此类依赖应该一直有效,Maven也不会去仓库中寻找它。
  • import(Maven2.0.9及以上)
    import范围只适用于pom文件中的部分。表明指定的POM必须使用部分的依赖。因为依赖已经被替换,所以使用import范围的依赖并不影响依赖传递。