Maven笔记

目标

一、为什么使用 Maven 这样的构建工具【why】

① 一个项目就是一个工程

如果项目非常庞大,就不适合使用 package 来划分模块,最好是每一个模块对应一个工程,利于分工协作。借助于 maven 就可以将一个项目拆分成多个工程

② 项目中使用 jar 包,需要“复制”、“粘贴”项目的 lib 中

同样的 jar 包重复的出现在不同的项目工程中,你需要做不停的复制粘贴的重复工作。借助于 maven,可以将 jar 包保存在“仓库”中,不管在哪个项目只要使用引用即可就行。

③ jar 包需要的时候每次都要自己准备好或到官网下载

借助于 maven 我们可以使用统一的规范方式下载 jar 包,规范

④ jar 包版本不一致的风险

不同的项目在使用 jar 包的时候,有可能会导致各个项目的 jar 包版本不一致,导致未执行错误。借助于 maven,所有的 jar 包都放在“仓库”中,所有的项目都使用仓库的一份 jar 包。

⑤ 一个 jar 包依赖其他的 jar 包需要自己手动的加入到项目中

FileUpload 组件->IO 组件,commons-fileupload-1.3.jar 依赖于 commons-io-2.0.1.jar

极大的浪费了我们导入包的时间成本,也极大的增加了学习成本。借助于 maven,它会自动的将依赖的 jar 包导入进来。

二、maven 是什么【what】

① maven 是一款服务于 java 平台的自动化构建工具

make->Ant->Maven->Gradle

名字叫法:我们可以叫妹文也可以叫麦文,但是没有叫妈文的。

② 构建

构建定义:把动态的 Web 工程经过编译得到的编译结果部署到服务器上的整个过程。

编译:java 源文件[.java]->编译->Classz 字节码文件[.class]

部署:最终在 sevlet 容器中部署的不是动态 web 工程,而是编译后的文件

image

③ 构建的各个环节

  • 清理 clean:将以前编译得到的旧文件 class 字节码文件删除

  • 编译 compile:将 java 源程序编译成 class 字节码文件

  • 测试 test:自动测试,自动调用 junit 程序

  • 报告 report:测试程序执行的结果

  • 打包 package:动态 Web 工程打 War 包,java 工程打 jar 包

  • 安装 install:Maven 特定的概念—–将打包得到的文件复制到“仓库”中的指定位置

  • 部署 deploy:将动态 Web 工程生成的 war 包复制到 Servlet 容器下,使其可以运行

三、安装 maven

① 当前系统是否配置 JAVA_HOME 的环境变量

② 下载 maven,解压 maven 放在一个非中文无空格的路径下

③ 配置 maven 的相关环境变量

  • 在环境变量增加 M2_HOME,路径是 maven 解压后的根目录

  • 在环境变量里的 path 中增加 maven/bin 的目录

④ 验证:maven -v 查看 maven 版本

看到版本信息,恭喜你已经 OK 了。

image

四、第一个 maven

① 创建约定的目录结构(maven 工程必须按照约定的目录结构创建)

根目录:工程名
|—src:源码
|—|—main:存放主程序
|—|—|—java:java 源码文件
|—|—|—resource:存放框架的配置文件
|—|—test:存放测试程序
|—pop.xml:maven 的核心配置文件

我们按照上面的文件夹目录结构手动创建一下,不用任何 IDE 环境(手动的其实最有助于我们理解 maven)

image

文件内容如下

在 src/main/java/com/hzg/maven 目录下新建文件 Hello.java,内容如下

package com.hzg.maven;
public class Hello {
  public String sayHello(String name){
    return "Hello "+name+"!";
  }
}

POM 文件内容:

<?xml version="1.0" ?>
<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.hzg.maven</groupId>
    <artifactId>Hello</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>Hello</name>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

② 常用 maven 命令

  • mvn clean:清理

  • mvn compile:编译主程序

  • mvn test-compile:编译测试程序

  • mvn test:执行测试

  • mvn package:打包

  • mvn install:安装

执行 maven 命令必须进入到 pom.xml 的目录中进行执行

image

进入到项目的 pom.xml 目录之后,就可以执行啦。

1、运行 mvn compile

image

OK,运行完毕,你在 pom.xml 配置的依赖的包已经导入到仓库了,问题来了,仓库默认的位置在哪?

仓库的默认位置:c:\Usrs[登录当前系统的用户名].m2\repository

刚才执行完 compile 之后,之前的文件夹发生了变化

image

我们发现 Hello 项目里里多了一个 target 文件夹。文件夹的内容为:

image

发现 target 里主要存放的就是编译后的字节码文件

2、运行 mvn test-compile,target 文件夹下面除了 classes 之外多了 test-classes 文件夹

3、运行 mvn package,target 文件夹下面又多了一个打好的 jar 包

image

4、运行 mvn clean,发现整个 target 文件夹都没了。又回到了编译之前我们手动创建的文件夹

image

五、仓库和坐标

①pom.xml:
Project Object Model 项目对象模型。它是 maven 的核心配置文件,所有的构建的配置都在这里设置。

② 坐标: 使用下面的三个向量在仓库中唯一的定位一个 maven 工程

image

③ maven 工程的坐标与仓库中路径的关系:

image

maven 坐标和仓库对应的映射关系:[groupId][artifactId][version][artifactId]-[version].jar

去本地仓库看一下此目录:org\springframework\spring-core\4.3.4.RELEASE\spring-core-4.3.4.RELEASE.jar

果然是完全对应的(默认仓库地址上面说过了哦,不要说不知道在哪,没事下面我们再说一下仓库)

④ 仓库

仓库的分类:

1、本地仓库: 当前电脑上的仓库,路径上已经说过了哦

2、远程仓库:

  • 私服:搭建在局域网中,一般公司都会有私服,私服一般使用 nexus 来搭建。具体搭建过程可以查询其他资料

  • 中央仓库:架设在 Internet 上,像刚才的 springframework 就是在中央仓库上

六、依赖

① maven 解析依赖信息时会到本地仓库中取查找被依赖的 jar 包

  • 对于本地仓库中没有的会去中央仓库去查找 maven 坐标来获取 jar 包,获取到 jar 之后会下载到本地仓库

  • 对于中央仓库也找不到依赖的 jar 包的时候,就会编译失败了

② 如果依赖的是自己或者团队开发的 maven 工程,需要先使用 install 命令把被依赖的 maven 工程的 jar 包导入到本地仓库中

举例:现在我再创建第二个 maven 工程 HelloFriend,其中用到了第一个 Hello 工程里类的 sayHello(String name)方法。我们在给 HelloFriend 项目使用 mvn compile 命令进行编译的时候,会提示缺少依赖 Hello 的 jar 包。怎么办呢?

到第一个 maven 工程中执行 mvn install 后,你再去看一下本地仓库,你会发现有了 Hello 项目的 jar 包。一旦本地仓库有了依赖的 maven 工程的 jar 包后,你再到 HelloFriend 项目中使用 mvn compile 命令的时候,可以成功编译

③ 依赖范围

image

scope 就是依赖的范围

1、compile, 默认值,适用于所有阶段(开发、测试、部署、运行),本 jar 会一直存在所有阶段。

2、provided, 只在开发、测试阶段使用,目的是不让 Servlet 容器和你本地仓库的 jar 包冲突 。如 servlet.jar。

3、runtime, 只在运行时使用,如 JDBC 驱动,适用运行和测试阶段。

4、test, 只在测试时使用,用于编译和运行测试代码。不会随项目发布。

5、system, 类似 provided,需要显式提供包含依赖的 jar,Maven 不会在 Repository 中查找它。

七、生命周期  

Maven 有三套相互独立的生命周期,请注意这里说的是“三套”,而且“相互独立”,初学者容易将 Maven 的生命周期看成一个整体,其实不然。这三套生命周期分别是:

① Clean Lifecycle 在进行真正的构建之前进行一些清理工作。 Clean 生命周期一共包含了三个阶段:

  • pre-clean 执行一些需要在 clean 之前完成的工作

  • clean 移除所有上一次构建生成的文件

  • post-clean 执行一些需要在 clean 之后立刻完成的工作

② Default Lifecycle 构建的核心部分,编译,测试,打包,部署等等。

  • validate

  • generate-sources

  • process-sources

  • generate-resources

  • process-resources 复制并处理资源文件,至目标目录,准备打包

  • compile 编译项目的源代码

  • process-classes

  • generate-test-sources

  • process-test-sources

  • generate-test-resources

  • process-test-resources 复制并处理资源文件,至目标测试目录

  • test-compile 编译测试源代码

  • process-test-classes

  • test 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署

  • prepare-package

  • package 接受编译好的代码,打包成可发布的格式,如 JAR

  • pre-integration-test

  • integration-test

  • post-integration-test

  • verify

  • install 将包安装至本地仓库,以让其它项目依赖。

  • deploy 将最终的包复制到远程的仓库,以让其它开发人员与项目共享

那我们在 Hello 的项目中执行 mvn install 命令,通过日志看看中间经历了什么?

image

通过日志我们发现,其实执行 mvn install,其中已经执行了 compile 和 test 。

总结: 不论你要执行生命周期的哪一个阶段,maven 都是从这个生命周期的开始执行

插件: 每个阶段都有插件(plugin),看上面标红的。插件的职责就是执行它对应的命令。

③ Site Lifecycle 生成项目报告,站点,发布站点。

  • pre-site 执行一些需要在生成站点文档之前完成的工作

  • site 生成项目的站点文档

  • post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备

  • site-deploy 将生成的站点文档部署到特定的服务器上

八、maven 工程的依赖高级特性

① 依赖的传递性

image

WebMavenDemo 项目依赖 JavaMavenService1 JavaMavenService1 项目依赖 JavaMavenService2

pom.xml 文件配置好依赖关系后,必须首先 mvn install 后,依赖的 jar 包才能使用。

  • WebMavenDemo 的 pom.xml 文件想能编译通过,JavaMavenService1 必须 mvn install

  • JavaMavenService 的 pom.xml 文件想能编译通过,JavaMavenService2 必须 mvn install

传递性:

image

在 Eclipse 中,为 JavaMavenService2 中增加了一个 spring-core.jar 包后,会惊喜的发现依赖的两个项目都自动的增加了这个 jar 包,这就是依赖的传递性。

注意:非 compile 范围的依赖是不能传递的。

② 依赖版本的原则:

1、路径最短者优先原则

image

Service2 的 log4j 的版本是 1.2.7 版本,Service1 排除了此包的依赖,自己加了一个 Log4j 的 1.2.9 的版本,那么 WebMavenDemo 项目遵守路径最短优先原则,Log4j 的版本和 Sercive1 的版本一致。

2、路径相同先声明优先原则

image

这种场景依赖关系发生了变化,WebMavenDemo 项目依赖 Sercive1 和 Service2,它俩是同一个路径,那么谁在 WebMavenDemo 的 pom.xml 中先声明的依赖就用谁的版本。

③ 统一管理依赖的版本:

image

为了统一管理版本号,可以使用 properties 标签,里面可以自定义版本的标签名。在使用的地方使用${自定义标签名}

十、build 配置

<build>
  <!-- 项目的名字 -->
  <finalName>WebMavenDemo</finalName>
  <!-- 描述项目中资源的位置 -->
  <resources>
    <!-- 自定义资源1 -->
    <resource>
      <!-- 资源目录 -->
      <directory>src/main/java</directory>
      <!-- 包括哪些文件参与打包 -->
      <includes>
        <include>**/*.xml</include>
      </includes>
      <!-- 排除哪些文件不参与打包 -->
      <excludes>
        <exclude>**/*.txt</exclude>
          <exclude>**/*.doc</exclude>
      </excludes>
    </resource>
  </resources>
  <!-- 设置构建时候的插件 -->
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>2.1</version>
      <configuration>
        <!-- 源代码编译版本 -->
        <source>1.8</source>
        <!-- 目标平台编译版本 -->
        <target>1.8</target>
      </configuration>
    </plugin>
    <!-- 资源插件(资源的插件) -->  
    <plugin>  
      <groupId>org.apache.maven.plugins</groupId>  
      <artifactId>maven-resources-plugin</artifactId>  
      <version>2.1</version>  
      <executions>  
        <execution>  
          <phase>compile</phase>  
        </execution>  
      </executions>  
      <configuration>  
        <encoding>UTF-8</encoding>  
      </configuration> 
    </plugin>
    <!-- war插件(将项目打成war包) -->  
    <plugin>  
      <groupId>org.apache.maven.plugins</groupId>  
      <artifactId>maven-war-plugin</artifactId>  
      <version>2.1</version>  
      <configuration>
        <!-- war包名字 -->  
        <warName>WebMavenDemo1</warName>
      </configuration>  
    </plugin>  
  </plugins>
</build>

配置好 build 后,执行 mvn package 之后,在 maven 工程指定的 target 目录里 war 包和文件都按照配置的生成了

image