Maven学习笔记

简述


Maven在我们的目前的工作中是使用最广泛的工具之一,它在我们编译,打包,部署等等各个环节都发挥着非常重要的作用,在敏捷开发中亦扮演中非常重要的角色,比如持续集成,持续交付。距离 Ant的时代已经过去 很久了,Maven诞生之际也已经很多年了,但是学习Maven并熟练掌握其中的技巧在工作中依旧非常重要。本文是我最近这些天重新学习整理的笔记,它不能让你成为Maven的高手,但是可以让你在平时工作中如果遇到 Maven相关问题时有一些解决思路,或者说大概知道从什么方向去思考如何解决实际问题。

作为开发者,我们在实际工作中可能对于Maven的感知仅在于我们当前项目下的POM文件Setting文件,除此之外再多就是公司提供的Nexus私服本地仓库等相关概念,那么入手学习也应该从这些最常见的概念中学习,逐步深入。

POM 文件


POM是指 Project Object Model,以XML的方式对项目进行约束与管理,原则上约定优于配置。

官方POM文件的入口:http://maven.apache.org/pom.html

对于POM文件的了解与学习,个人建议是梳理POM文件比较常见的配置项即可,同时如果发现有不清楚的配置项,学会如何从官网了解并从demo中入手学会即可。

配置项 描述 案例
groupId <groupId>org.cn.lennon</groupId>
artifactId 功能模块的名称 <artifactId>api</artifactId>
version 版本号 <version>0.2.1-RELEASES</version>
packaging 打包方式,默认为jar <packaging>pom</packaging>
description 项目描述 <description>lennon 独立开发者提供开源产品</description>
properties 配置项,用于定义变量,常见定义版本号 <project-version>${project.version}</project-version>
parent 父模块信息,常见于各种第三方模块库 <parent><groupId>....</groupId></parent>
dependencies 依赖管理 <dependencies></dependencies>
dependency 依赖 <dependency></dependency>
dependencyManagement 依赖管理 <dependencyManagement ></dependencyManagement >
modules 子模块 <modules> ... </ modules>
build 基础构建参数设置 <build> ... </ build>
distributionManagement 经常用于配置连接私服,在打包完成部署后将会提交至私服仓库中 <distributionManagement> ... </ distributionManagement>

注意事项

1、dependencyManagement与dependencies有何区别?

dependencyManagement常用于父POM对于子模块的依赖管理,所以一般只用于父POM文件

  • 主要用以统一管理各个依赖的版本号,避免同一个项目对于同一个依赖出现不同版本导致一些版本不兼容之类的问题

  • 用于声明依赖(子模块依赖父POM的引用),将所有的外部包统一到父POM文件,以便于管理维护

2、dependency中的scope详解

依赖jar的作用域,它要解决的关键问题在于什么阶段会被使用,以及会不会被打包

作用域 功能 描述
compile 编译,测试,打包(默认值) 该作用域表示被依赖jar需要参与当前项目的编译,测试,运行,打包等生命周期,可以说它会参与整个生命周期。属于强依赖关系。
test 测试 仅参与测试阶段的相关工作,包括测试代码的编译与执行,但是不会被打包到war/jar中
provided 编译,测试 仅参与编译,测试,但是不参与打包以及打包后环境的运行,由第二方/第三方提供。即在打包阶段做了excloud操作
runtime 打包 不参与项目的编译,但是后期的测试与运行都需要参与,除了编译阶段其他阶段均参与,比如JDBC驱动。
system 编译,测试,打包 从参与度来说,和provided相同,不过被依赖项不会从maven仓库下载,而是从本地文件系统拿。需要添加systemPath的属性来定义路径。(不推荐)

3、传递依赖

第一列表示直接依赖的scope,第一行表示间接依赖的scope

image.png

假如有Maven项目A,项目B依赖A,项目C依赖B。那么我们可以说 C依赖A。也就是说,依赖的关系为:C—>B—>A。那么我们执行项目C时,会自动把B、A都下载导入到C项目的jar包文件夹中。这就是依赖的传递性。

4、依赖冲突(依赖仲裁)

  • 最短路径原则,相同包选择最短的依赖路劲
image.png

决策在于依赖树长短,以依赖树短为主,谁短依赖谁。上图则说明依赖于X 0.1-R的版本

  • 加载先后原则,xml的书写顺序,先声明优先
image.png

决策在于在项目A的POM文件中,<depencies>B</depencies>先还是<depencies>C</depencies>先,谁先写就先依赖谁。

5、 依赖传递中排除

如上,C—>B—>A。加入现在不想执行C时把A下载进来,那么我们可以用 <exclusions>标签。


<dependencies>

    <dependency>

        <groupId>B</groupId>

        <artifactId>B</artifactId>

        <version>0.0.1</version>

         <exclusions>

            <exclusion>

              <!--被排除的依赖包坐标-->

              <groupId>A</groupId>

              <artifactId>A</artifactId>

              <version>0.0.1</version>

            </exclusion>

         </exclusions>

    </dependency>

</dependencies>

6、版本管理

官方版本号格式参考:主版本号.次版本号.增量版本号-<里程碑版本> eg: 1.0.0-RELAESE

  • SNAPSHOT不稳定版本(迭代版本)

  • RELEASE 稳定版本

发布新的版本,如果没有更新jar的版本号,如何依赖到最新的版本?

  • repository将旧的jar包删除

  • mvn clean package -U(强制拉取最新的版本,在没有更新版本好的情况下。版本发布常用)

7、profile

pom常见使用环境变量:dev/local/prod


  <profiles>

      <profile>

          <id>dev</id>

          <activation>

              <activeByDefault>true</activeByDefault>

          </activation>

          <properties>

              <profiles.active>dev</profiles.active>

          </properties>

      </profile>

      <profile>

          <id>local</id>

          <properties>

              <profiles.active>local</profiles.active>

          </properties>

      </profile>

  </profiles>

<build>

        <resources>

            <resource>

                <directory>${basedir}/src/main/resources</directory>

                <excludes>

                    <exclude>conf/**</exclude>

                </excludes>

            </resource>

            <resource>

                <directory>${basedir}/src/main/resources/conf/${profiles.active}</directory>

            </resource>

        </resources>

</build>

命令:mvn clean package -P local

Setting文件


从download下来的maven工具中就已经包含了相应的setting.xml文件,位于conf目录下。

基础配置项

setting.xml文件打开是可以看到,对于每一个配置项都做了注释,有需要便开启并配置相应参数,非常人道。常见的配置项如下:

配置项 功能
localRepository 本地仓库
servers 服务器认证信息
mirrors 仓库镜像
profiles 配置项,用于配置不同环境下不同使用参数
activeProfiles 用于决定启用那些配置项

profiles


<profile>

  <id>jdk18</id>

  <activation>

   <jdk>1.8</jdk>

   <activeByDefault>true</activeByDefault>

  </activation>

  <properties>

   <maven.compiler.source>1.8</maven.compiler.source>

   <maven.compiler.target>1.8</maven.compiler.target>

   <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>

  </properties>

 </profile>

 <profile>

   <id>nexus</id>

   <repositories>

    <repository>

     <id>central</id>

     <url>http://central</url>

     <releases><enabled>true</enabled></releases>

     <snapshots><enabled>true</enabled></snapshots>

    </repository>

   </repositories>

   <pluginRepositories>

    <pluginRepository>

     <id>central</id>

     <url>http://central</url>

     <releases><enabled>true</enabled></releases>

     <snapshots><enabled>true</enabled></snapshots>

    </pluginRepository>

   </pluginRepositories>

  </profile>

  </profiles>

<!--  启用哪个配置项  -->

  <activeProfiles>

    <activeProfile>jdk18</activeProfile>

    <activeProfile>nexus</activeProfile>

  </activeProfiles>

Maven 生命周期


image.png

概念

Mavenue的生命周期有三个关键概念需要理解

  • lifecycle 生命周期

  • phase 阶段

  • goals 目标

关于如何理解该三个概念,讲的最透彻的大概就是下边两个句子了:

  1. A Build Lifecycle is Made Up of Phases

  2. A Build Phase is Made Up of Plugin Goals

关于生命周期的博客参考:

http://juvenshun.iteye.com/blog/213959

https://www.cnblogs.com/EasonJim/p/6816340.html

常用命令

命令其实就是体现在maven生命周期中的phase,可以说是maven内置的插件。

  • compile 编译代码

  • clean 删除target/

  • test test case junit/testNG

  • package 打包

  • install 把项目install到local repo

  • deploy 发本地jar发布到remote

插件


常用插件

https://maven.apache.org/plugins/

http://www.mojohaus.org/plugins.html

插件名称 功能 使用方式
versions 统一处理版本号 versions:help; versions:set -DnewVersion=1.1.1-release;
source 源代码管理 source:help;source:jar
assembly 打包 。。。
tomcat7 内置tomcat环境 tomcat:run; tomcat:help;

spring boot是如何做到jar启动?

从原理上去分析,其实本质是换汤不换药的,其实利用的就是maven的Tomcat插件做到的,怎么做到的呢?

  • 从start.spring.io中load一个web应用,最包含约定好的入口类,打开pom可以看到其中插件上有

<plugins>

  <plugin>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-maven-plugin</artifactId>

  </plugin>

</plugins>


而这个插件又做了什么呢?

通过https://docs.spring.io/spring-boot/docs/1.5.4.RELEASE/maven-plugin/plugin-info.html进入该官网页面可以看到该插件相应的goals

  • 将load下来的应用进行打包:mvn package后,使用jd-gui对包进行反编译可以看到如下目录
image.png

通过查看MANIFEST.MF文件可以看到其中有两个配置:


Main-Class: org.springframework.boot.loader.JarLauncher

Start-Class: com.example.springbootdemo.SpringBootDemoApplication

  • 我们知道maven-assembly插件就有一个mainClass,打包jar的入口类的配置,那么查看该Main-Class的类呢?

  • 翻开spring-boot的配置文件可以看到mainClass作为主类入口

image.png

自定义插件

https://maven.apache.org/guides/plugin/guide-java-plugin-development.html

  • 自定义插件时,必须将packaging申明为maven-plugin

  <groupId>org.cn.lennon</groupId>

  <artifactId>lennon-maven-plugin</artifactId>

  <version>1.0-SNAPSHOT</version>

  <packaging>maven-plugin</packaging>

  • 自定义插件时,类需要实现AbstractMojo

// mojo的name就是指goals

@Mojo( name = "sayhi", defaultPhase = LifecyclePhase.PACKAGE)

public class GreetingMojo extends AbstractMojo {

     @Parameter

    private String title;

    public void execute() throws MojoExecutionException {

        getLog().info( "Hello, world." );

        getLog().info( "Hello, world." + title );

    }

}

  • 如何使用自定义插件呢?

<plugin>

   <groupId>org.cn.lennon</groupId>

   <artifactId>lennon-maven-plugin</artifactId>

   <version>1.0-SNAPSHOT</version>

 <configuration>

      <title>test1</title>

 </configuration>

   <executions>

       <execution>

         <phase>package</phase>

          <goals>

                      <goal>sayhi</goal>

           </goals>

         </execution>

        </executions>

 </plugin>

  • 运行:mvn package

重新认识maven的lifecycle\phase\goals

lifecycle是指maven的整个生命周期,而在整个生命周期下分为三个阶段,统称为phase。在命令行中常常可见的方式比如mvn packagepackage就是一个阶段。每执行一个阶段下就会有不同的goals。比如说我们将自定义好的插件挂载package阶段下,当我们执行该阶段时就会执行该插件。

理解了phasegoals的关系后,对于理解整个maven来说异常重要。

私服


私服搭建需要的资料从官方下载 https://www.sonatype.com/oss-thank-you-win64.zip

概念

  • hosted 表示上传的仓库,可以考虑创建两三个hosted类型的仓库

1、releases 用于上传releases版本的相关jar(稳定版本)

2、snapshots 用于上传snapshots版本的相关jar(迭代版本,不稳定版本)

3、3thparty 用户上传第三方jar包(可选择)

  • proxy 表示当从私服下载不到jar包时,将会从proxy类型的仓库下载

  • group 表示可以将多个仓库合并成一个。

搭建推荐方式:

image.png

proxy为阿里云的maven仓库地址。public为对外的公共仓库地址。

setting.xml配置方式


<servers>

 <server>

  <id>nexus</id>

  <username>admin</username>

  <password>admin123</password>

 </server>

</servers>

  <mirror>

  <id>nexus</id>

  <mirrorOf>*</mirrorOf>

  <url>http://localhost:8081/repository/nexus-public/</url>

 </mirror>

<profile>

   <id>nexus</id>

   <repositories>

    <repository>

     <id>central</id>

     <url>http://central</url>

     <releases><enabled>true</enabled></releases>

     <snapshots><enabled>true</enabled></snapshots>

    </repository>

   </repositories>

   <pluginRepositories>

    <pluginRepository>

     <id>central</id>

     <url>http://central</url>

     <releases><enabled>true</enabled></releases>

     <snapshots><enabled>true</enabled></snapshots>

    </pluginRepository>

   </pluginRepositories>

 </profile>

pom.xml


    <distributionManagement>

        <repository>

            <id>nexus</id>

            <name>nexus-releases</name>

            <url>http://localhost:8081/repository/nexus-releases/</url>

        </repository>

        <snapshotRepository>

            <id>nexus</id>

            <name>nexus-snapshots</name>

            <url>http://localhost:8081/repository/nexus-snapshots/</url>

        </snapshotRepository>

    </distributionManagement>


一些可能作为身为高级工程师或者架构师需要去做的事


私服

前文中已经讲过Nexus私服搭建,这不再赘述

项目模块化

项目模块分区分后的种种好处不言而喻,参考Apache RocketMQ的目录结构

  • 父POM文件中的packaging设置为pom,如:<packaging>pom</packaging>

  • 新增模块时,将模块引入到modules

  • 将项目依赖都放入dependencyManagement中管理

  • 子模块配置parent,依赖可以引用自父POM,不加版本号。

  • 如果使用IDE的话,可以右键创建Maven Module项目

archetype 模板化

在平时开发中,创建一个Maven项目时,都会优先选择一个脚手架,比如常用的maven-archetype-quickstart,但是到了 不同的公司具备了不同的业务场景以及规范,maven提供的通用模板可能不够细致,在微服务盛行的今天,一个项目依赖的应用粒度越细,那么需要的应用就越多,为了统一管理维护,自然需要创建一个合规合理的脚手架以帮助开发者快速创建项目基本架构并快速投入业务开发中。

  • 通过IDE快速创建一个脚手架项目,声明:<packaging>maven-archetype</packaging>

  • 按照业务与规范将基本的内容填充好

  • mvn archetype:create-from-project

  • cd /target/generated-sources/archetype,执行mvn install

  • 从archetype创建项目 mvn archetype:generate -DarchetypeCatalog=local

完成可以本地IDE上看到该脚手架,Catalog选择Default Catalog便可看到自己的创建好的脚手架,确认无误后可以推广至整个研发团队,以帮助开发者快速建立maven项目。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Maven概述 Maven定义Maven是一个项目管理和整合,统一管理jar包的工具;Maven为开发者提供了一套...
    THQ的简书阅读 4,187评论 0 0
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 47,084评论 6 342
  • 简介 概述 Maven 是一个项目管理和整合工具 Maven 为开发者提供了一套完整的构建生命周期框架 Maven...
    闽越布衣阅读 9,816评论 6 39
  • 引言:Mavne相必大多数java开发者都比较熟悉;可以将它理解为一个项目的管理工具,它是一个可以两三行代码就可构...
    cp_insist阅读 2,383评论 0 0
  • 1.“你有多久没有看到漫天的繁星,城市夜晚虚伪的光明遮住你的眼睛。” 2.“爱情这东西,你已经不再有勇气。情歌有多...
    mg_51de阅读 1,540评论 0 0