Maven

为什么需要Maven

目前的技术在开发中存在的问题:

  1. 一个项目就是一个工程

    如果项目非常庞大,就不适合继续使用package来划分模块,最好是每一个模块对应一个工程,利于分工协作。

    借助Maven就可以将一个项目拆分成多个工程。

  2. 项目中所需要的jar包必须手动复制粘贴到WEB-INF/lib目录下

    带来的问题是:同样的jar包文件重复出现在不同的项目工程中,一方面浪费存储空间,另外也让工程比较臃肿。

    借助Maven,可以将jar包仅仅保存在仓库中,有需要使用的工程引用这个文件接口,并不需要真的把jar包复制过来。

  3. jar包需要别人替我们准备好或到官网下载

    不同技术的官网提供jar包下载的形式是五花八门的,有些技术的官网就是通过Maven或SVN等专门的工具来提供下载的。

    如果是以不规范的方式下载的jar包,那么其中的内容很可能也是不规范的。

    借助于Maven可以以一种规范的方式下载jar包,因为所有知名框架或第三方工具的jar包以及按统一的规范存放在了Maven的中央仓库中。

    以规范的方式下载的jar包,内容也是可靠的。

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

    FileUpload组件→IO组件。commons-fileupload-1.3.jar依赖于commons-io-2.0.1.jar

    如果所有jar包之间的依赖关系都需要程序员自己非常清楚的了解,那么就会极大地增加学习成本

    Maven会自动将被依赖的jar包导入进来

Maven是什么

  1. Maven是一款服务于Java平台的自动化构建工具

    Make→Ant→Maven→Gradle

  2. 构建

    概念:以"Java源文件"、"框架配置文件"、"JSP"、"HTML"、"图片"等资源为原材料,去生产一个可以运行的项目的过程

    构建过程中的各个环节

    1. 清理:将以前编译得到的旧的class字节码文件删除,为下一次编译做准备
    2. 编译:将Java源程序编译成class字节码文件
    3. 测试:自动测试,自动调用junit程序
    4. 报告:测试程序执行的结果
    5. 打包:动态Web工程打war包,Java工程打jar包
    6. 安装:Maven特定的概念——将打包得到的文件复制到仓库中的指定位置
    7. 部署:将动态Web工程生成的war包复制到Servlet容器的指定目录下,使其可以运行

常用Maven命令

执行与构建过程相关的Maven命令,必须进入pom.xml所在的目录与构建过程相关:编译、测试、打包

常用命令

mvn clean //清理
mvn compile //编译主程序
mvn test-compile //编译测试程序
mvn test //执行测试
mvn package //打包

联网问题

  1. Maven的核心程序中仅仅定义了抽象的生命周期,但是具体的工作必须由特定的插件来完成,而插件本身并不包含在Maven的核心程序中

  2. 当我们执行的Maven命令需要用到某些插件时,Maven核心程序会首先到本地仓库中查找

  3. 本地仓库的默认位置:[系统中当前用户的家目录]\.m2\repository

  4. Maven核心程序如果在本地仓库中找不到需要的插件,那么它就会自动连接外网,到中央仓库下载

  5. 如果此时无法连接外网,则构建失败

  6. 修改默认本地仓库的位置可以让Maven核心程序到我们事先准备好的目录下查找插件

    [1]找到Maven解压目录\conf\settings.xml

    [2]在settings.xml文件中找到localRepository标签

    [3]将/path/to/local/repo从注释中取出

    [4]将标签体内容修改为已经准备好的Maven仓库目录

    <localRepository>D:\RepMaven</localRepository>
    

POM

  1. 含义:Project Object Model 项目对象模型
  2. pom.xml对于Maven工程是核心配置文件,与构建过程相关的一切设置都在这个文件中进行配置

坐标

使用下面三个向量在仓库中唯一定位一个Maven工程

  1. groupid:公司或组织域名倒序+项目名

    <groupid>com.atguigu.maven</groupid>
    
  2. artifactid:模块名

    <artifactid>Hello</artifactid>
    
  3. version:版本

    <version>1.0.0</version>
    

Maven工程的坐标与仓库中路径的对应关系

<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>

org/springframework/spring-core/4.0.0.RELEASE/spring-core-4.0.0.RELEASE.jar

仓库

  1. 分类

    [1]本地仓库:当前本机电脑上的所有Maven工程服务远程仓库

    [2]远程仓库:

    1. ***:架设在当前局域网环境下,为当前局域网范围内的所有Maven工程服务

    2. 中央仓库:架设在Internet上,为全世界所有Maven工程服务

    3. 中央仓库的镜像:架设在各个大洲,为中央仓库分担流量

      减轻中央仓库的压力,同时更快的响应用户请求

  2. 仓库中保存的内容:Maven工程

    [1]Maven自身所需要的插件

    [2]第三方框架或工具的jar包

    [3]我们自己开发的Maven工程

依赖

  1. Maven解析依赖信息时会到本地仓库中查找被依赖的jar包

    对于我们自己开发的Maven工程,使用mvn install命令安装后就可以进入仓库

  2. 依赖的范围

    [1]compile范围依赖

    对主程序是否有效:有效

    对测试程序是否有效:有效

    是否参与打包:参与

    [2]test范围依赖

    对主程序是否有效:无效

    对测试程序是否有效:有效

    是否参与打包:不参与

    典型例子:junit

    [3]provided范围依赖

    对主程序是否有效:有效

    对测试程序是否有效:有效

    是否参与打包:不参与

    是否参与部署:不参与

    典型例子:servlet-api.jar

  3. 依赖的传递性

    [1]好处:可以传递的依赖不必在每个模块工程中都重复声明,在最下面的工程中依赖一次即可

    image-20220203041824026

    [2]注意:非compile范围的依赖不能传递,所以在各个工程模块中,如果有需要就得重复声明依赖

  4. 依赖的排除

    需要设置排除依赖的场合

    image-20220203042307854

    依赖排除的设置方式

    <exclusions>
        <exclusion>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
        </exclusion>
    </exclusions>
    
  5. 依赖的原则

    [1]作用:解决模块工程之间的jar包冲突问题

    [2]情景设定1:验证路径最短者优先原则

    image-20220203042739637

    [3]情景设定2:验证路径相同时先声明者优先

    image-20220203042827450

    先声明指的是dependency标签的声明顺序

  6. 依赖的修改

    如果有个项目对某个框架的各个jar包的依赖版本都是4.0.0

    需要统一升级为4.1.1,怎么办?手动逐一修改不可靠

    建议配置方式:

    1. 使用properties标签内使用自定义标签统一声明版本号

      <properties>
          <atguigu.spring.version>		
          	4.0.0.RELEASE
          </atguigu.spring.version>
      </properties>
      
    2. 在需要统一版本的位置,使用${自定义标签名}引用声明的版本号

      <version>${atguigu.spring.version}</version>
      
    3. 其实properties标签配合自定义标签声明数据的配置并不是只能用于声明依赖的版本号,凡是需要统一声明后再引用的场合都可以使用

      <properties>
          <atguigu.spring.version>
          	4.1.1.RELEASE
          </atguigu.spring.version>
          <project.build.sourceEncoding>
          	UTF-8
          </project.build.sourceEncoding>
      </properties>
      

生命周期

各个构建环节执行的顺序:不能打乱顺序,必须按照既定的正确顺序来执行;Maven的核心程序中定义了抽象的生命周期,生命周期中各个阶段的具体任务是由插件来完成的;Maven核心程序为了更好的实现自动化构建,按照这一特点执行生命周期的各个阶段:不论现在要执行生命周期的哪一个阶段,都从这个生命周期最初的位置开始执行。

生命周期阶段 插件目标 插件
compile compile maven-compiler-plugin
test-compile testCompile maven-compiler-plugin

继承

  1. 现状

    Hello依赖的junit:4.0

    HelloFriend依赖的junit:4.0

    MakeFriends依赖的junit:4.9

    由于test范围的依赖不能传递,所以必然会分散在各个模块工程中,很容易造成版本不一致

  2. 需求:统一管理各个模块工程中对junit依赖的版本

  3. 解决思路:将junit依赖版本统一提取到父工程中,在子工程中声明junit依赖时不指定版本,以父工程中统一设定的为准

  4. 操作步骤

    [1]创建一个Maven工程作为父工程。注意:打包的方式pom

    <groupId>com.atguigu.maven</groupId>
    <artifactId>Parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    

    [2]在子工程中声明对父工程的引用

    <!-- 子工程中声明父工程 -->
    <parent>
        <groupId>com.atguigu.maven</groupId>
        <artifactId>Parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        
    <!-- 以当前文件为基准的父工程pom.xml文件的相对路径 -->
       <relativePath>../Parent/pom.xml</relativePath>
    </parent>
    

    [3]将子工程的坐标中与父工程坐标中重复的内容删除

    [4]在父工程中统一junit的依赖

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

    [5]在子工程中删除junit依赖的版本号部分

  5. 注意:配置继承后,执行安装命令时要先安装父工程

聚合

  1. 作用:一键安装各个模块功能

  2. 配置方式:在一个总的聚合工程中配置各个参与聚合的模块

    <!-- 配置聚合 -->
    <modules>
    	<!-- 指定各个工程的相对路径 -->
        <module>../Hello</module>
        <module>../HelloFriend</module>
        <module>../MakeFriends</module>
    </modules>
    
  3. 使用方式:在聚合工程的pom.xml上点右键→run as→maven install

生命周期

什么是Maven的生命周期?

Maven生命周期定义了各个构建环节的执行顺序,有了这个清单,Maven就可以自动化的执行构建命令了。

Maven有三套相互独立的生命周期,分别是:

  1. Clean Lifecycle 在进行真正的构建之前进行一些清理工作
  2. Default Lifecycle 构建的核心部分,编译,测试,打包,安装,部署等等
  3. Site Lifecycle 生成项目报告,站点,发布站点

他们是相互独立的,你可以仅仅调用clean来清理工作目录,仅仅调用site来生成站点,当然你也可以直接运行mvn clean install site运行所有这三套生命周期。

每套生命周期都由一组阶段(Phase)组成,我们平时在命令行输入的命令总会对应一个特定的阶段,比如运行mvn clean,这个clean是clean生命周期的一个阶段,有clean生命周期,也有clean阶段

Clean生命周期

Clean生命周期一共包含了三个阶段

  1. pre-clean 执行一些需要在clean之前完成的工作
  2. clean 移除所有上次构建生成的文件
  3. post-clean执行一些需要在clean之后立刻完成的工作

Site生命周期

  1. pre-site执行一些需要在生成站点文档之前完成的工作
  2. site生成项目的站点文档
  3. post-site执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
  4. site-deploy将生成的站点文档部署到特定的服务器上

这里经常用到的是site阶段和site-deploy阶段,用以生成和发布Maven站点,这可是Maven相当强大的功能,Manager比较喜欢,文档及统计数据自动生成,很好看。

Default生命周期

Default生命周期是Maven生命周期中最重要的一个,绝大多数工作都发生在这个生命周期中。这里,只解释一些比较重要和常用的阶段:

validate

generate-sources

process-sources

generate-resources

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

compile 编译项目的源代码

process-classes

generate-test-sources

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

test-compile 编译测试源代码

process-test-classes

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

prepare-package

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

pre-integration-test

integration-test

生命周期

  1. 各个构建环节执行的顺序:不能打乱顺序,必须按照既定的正确顺序来执行。
  2. Maven的核心程序中定义了抽象的生命周期,生命周期中各个阶段的具体任务是由插件来完成的。
  3. Maven核心程序为了更好的实现自动化构建,按照这一特点执行生命周期中的各个阶段:不论现在要执行生命周期中的哪个阶段,都是从这个生命周期最初的位置开始执行。

统一管理依赖的版本号

建议配置方式:

  1. 使用properties标签内使用自定义标签统一声明版本号

    <properties>
    	<ethic.spring.version>4.0.0.RELEASE</ethic.spring.version>
    </properties>
    
  2. 在需要统一版本的位置,使用${自定义标签名}引用声明的版本号

    <dependency>
    	<groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${ethic.spring.version}</version>
    </dependency>
    

Maven Web工程的自动部署

<!--  配置当前工程构建中的特殊设置-->
  <build>
    <finalName>maven</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<!--配置构建过程中需要使用的插件-->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>

<!--          针对插件进行的配置-->
          <configuration>
            <container>

            </container>
          </configuration>

<!--          配置插件在什么情况下执行-->
          <executions>
            <execution>
              <id>cargo-run</id>
<!--              生命周期的阶段-->
              <phase>install</phase>
              <goals>
<!--                插件的目标-->
                <goal>run</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

补充

我们可以到http://mvnrepository.com/搜索需要的jar包依赖的信息