关于maven的一些问题

关于maven

Maven 是 Apache 组织下的一个跨平台的项目管理工具,主要服务于基于Java平台的项目构建,项目管理和项目信息管理,提供了帮助管理构建、文档、报告、依赖、scms、发布、分发的方法。可以方便的编译代码、进行依赖管理、管理二进制库等等。Maven 提供了标准的软件生命周期模型和构建模型,通过配置就能对项目进行全面的管理。它的跨平台性保证了在不同的操作系统上可以使用相同的命令来完成相应的任务。Maven 将构建的过程抽象成一个个的生命周期过程,在不同的阶段使用不同的已实现插件来完成相应的实际工作,这种设计方法极大的避免了设计和脚本编码的重复,极大的实现了复用。

maven的灵魂POM

pom全称为Project Object Model, 简单说就是要对构建的项目进行建模,将要构建的项目看成是一个对象Object,这个对象就会有相应的属性,属性在maven中用坐标表示,坐标元素包括groupId、artifactId、version、packaging、classfier。

1、坐标

元素 描述 ext
groupId 定义当前模块隶属的实际Maven项目, 表示方式与Java包类似 groupId不应直接对应项目隶属的公司/组织(一个公司/组织下可能会有很多的项目)
artifactId 定义实际项目中的一个Maven模块 推荐使用项目名作为artifactId前缀, 因为Maven打包默认以artifactId作为前缀
version 定义当前项目所处版本(如1.0-SNAPSHOT、4.2.7.RELEASE、1.2.15、14.0.1-h-3 等) Maven版本号定义约定: <主版本>.<次版本>.<增量版本>-<里程碑版本>
packaging 定义Maven项目打包方式, 通常打包方式与所生成构件扩展名对应 有jar(默认)、war、pom、maven-plugin等
classifier 用来帮助定义构建输出的一些附属构件(如javadoc、sources) 不能直接定义项目的classifier(因为附属构件不是由项目默认生成, 须有附加插件的帮助)

2、依赖

Maven最著名的(也是几乎每个人最先接触到的)就是Maven的依赖管理, 它使得我们不必再到开源项目的官网一个个下载开源依赖包, 然后再一个个放入classpath. 一个依赖声明可以包含如下元素:

元素 描述 ext
groupId、artifactId和version 依赖的基本坐标(最最重要)
type 依赖的类型, 对应于项目坐标定义的packaging 默认jar
scope 依赖的范围, 用来控制依赖与三种classpath(编译classpath、测试classpath、运行classpath)的关系 包含compile、provided、runtime、test、system和import 6种
optional 依赖是否可选 假如一个Jar包支持MySQL与Oracle两种DB, 因此其构建时必须添加两类驱动, 但用户使用时只会选择一种DB. 此时对A、B就可使用optional可选依赖
exclusions 排除传递性依赖 传递性依赖极大的简化了项目依赖的管理, 但也会引入Jar包版本冲突等问题, 此时就需要对传递性依赖进行排除

重点注意

1、依赖关系
  • 传递依赖:如果我们的项目引用了一个Jar包,而该Jar包又引用了其他Jar包,那么在默认情况下项目编译时,Maven会把直接引用和简洁引用的Jar包都下载到本地。
  • 排除依赖:如果我们只想下载直接引用的Jar包,那么需要在pom.xml中做如下配置:(将需要排除的Jar包的坐标写到下面)
 <exclusions>
      <exclusion>
             <groupId>io.lettuce</groupId>
             <artifactId>lettuce-core</artifactId>
      </exclusion>
 </exclusions>

若项目中多个Jar同时引用了相同的Jar时,会产生依赖冲突,但Maven采用了两种避免冲突的策略,因此在Maven中是不存在依赖冲突的。

  • 短路优先
    若本项目引用了A.jar,A.jar又引用了B.jar,B.jar又引用了X.jar,并且C.jar也引用了X.jar。在此时,Maven只会引用引用路径最短的Jar。
  • 声明优先:若引用路径长度相同时,在pom.xml中谁先被声明,就使用谁。
2、依赖范围

其中依赖范围scope用来控制依赖和编译,测试,运行的classpath的关系,主要的是四种依赖范围如下:

  • compile:默认编译依赖范围。对于编译,测试,运行三种classpath都有效
  • test:测试依赖范围。只对测试的classpath有效
  • provided:已提供的依赖范围。对于编译,测试的classpath都有效,但对于运行无效。因为已经由容器提供。例如:servlet-api
  • runtime:运行时提供。例如:jdbc驱动
3、依赖包Snapshot版本与Release版本

Snapshot版本代表不稳定、尚处于开发中的版本 ;
Release版本则代表稳定的版本 。

1、什么情况下该用SNAPSHOT?
协同开发时,如果A依赖构件B,由于B会更新,B应该使用SNAPSHOT来标识自己

  • 如果B不用SNAPSHOT,而是每次更新后都使用一个稳定的版本,那版本号就会升得太快,每天一升甚至每个小时一升,这就是对版本号的滥用。
  • 如果B不用SNAPSHOT, 但一直使用一个单一的Release版本号,那当B更新后,A可能并不会接收到更新。因为A所使用的repository一般不会频繁更新release版本的缓存(即本地repository),所以B以不换版本号的方式更新后,A在拿B时发现本地已有这个版本,就不会去远程Repository下载最新的B。

2、不用Release版本,在所有地方都用SNAPSHOT版本行不行?
不行,正式环境中不得使用snapshot版本的库。 比如说,今天你依赖某个snapshot版本的第三方库成功构建了自己的应用,明天再构建时可能就会失败,因为今晚第三方可能已经更新了它的snapshot库。你再次构建时,Maven会去远程repository下载snapshot的最新版本,你构建时用的库就是新的jar文件了,这时正确性就很难保证了。稳定版应该依赖Release版本的jar包,这样更新时需要更新版本号,版本号没变时,依赖的jar包也不会变。

构建

项目的构建过程对应的是PO对象的build属性,对应pom.xml中也就是元素中的内容。
在maven中一个构建过程就对应一个Lifecycle,即生命周期。这个Lifecycle也分为多个阶段,每个阶段叫做phase。一个标准的构建Lifecycle包含了如下的phase:

  • validate: 用于验证项目的有效性和其项目所需要的内容是否具备
  • initialize:初始化操作,比如创建一些构建所需要的目录等
  • generate-sources:用于生成一些源代码,这些源代码在compile phase中需要使用到
  • 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:打包
  • pre-integration-test:集成测试前置操作
  • integration-test:集成测试
  • post-integration-test:集成测试后置操作
  • install:将打包产物安装到本地maven仓库
  • deploy:将打包产物安装到远程仓库

在maven中,你执行任何一个phase时,maven会将其之前的phase都执行。例如 mvn install,那么maven会将deploy之外的所有phase按照他们出现的顺序一次执行。
上面Lifecycle的定义,也就是说maven为程序的构建定义了一套规范流程:第一步需要validate,第二步需要initialize… … compile,test,package,… … install,deploy,但是并没有定义每一个phase具体应该如何操作。这里的phase的作用有点类似于Java语言中的接口,只协商了一个契约,但并没有定义具体的动作。比如说compile这个phase定义了在构建流程中需要经过编译这个阶段,但没有定义应该怎么编译(编译的输入是什么?用什么编译javac/gcc?)。这里具体的动作就是由goal来定义,一个goal在maven中就是一个Mojo(Maven old java object)。Mojo抽象类中定义了一个execute()方法,一个goal的具体动作就是在execute()方法中实现。实现的Mojo类应该放在哪里呢?答案是maven plugin里,所谓的plugin其实也就是一个maven项目,只不过这个项目会引用maven的一些API,plugin项目也具备maven坐标。

     <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.basedir}/libs</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
       </plugins>
    </build>

在执行具体的构建时,我们需要为lifecycle的每个phase都绑定一个goal,这样才能够在每个步骤执行一些具体的动作。比如在lifecycle中有个compile phase规定了构建的流程需要经过编译这个步骤,而maven-compile-plugin这个plugin有个compile goal就是用javac来将源文件编译为class文件的,我们需要做的就是将compile这个phase和maven-compile-plugin中的compile这个goal进行绑定,这样就可以实现Java源代码的编译了。那么有人就会问,在哪里绑定呢?答案是在pom.xml元素中配置即可。
就将maven-dependency-plugin中的copy-dependencies这个goal绑定到了prepare-package这个phase,后续在maven执行到prepare-package phase时就会执行copy-dependencies goal。

仓库

Maven仓库可简单分成两类: 本地仓库与远程仓库. 当Maven根据坐标寻找构件时, 它会首先检索本地仓库, 如果本地存在则直接使用, 否则去远程仓库下载.

  • 本地仓库: 默认地址为~/.m2/, 一个构件只有在本地仓库存在之后, 才能由Maven项目使用.
  • 远程仓库: 远程仓库又可简单分成两类: 中央仓库和私服. 由于原始的本地仓库是空的, Maven必须至少知道一个远程仓库才能在执行命令时下载需要的构件, 中央仓库就是这样一个默认的远程仓库.
私服

私服是一种特殊的远程仓库, 它设在局域网内, 通过代理广域网上的远程仓库, 供局域网内的Maven用户使用.
当需要下载构件时, Maven客户端先向私服请求, 如果私服不存在该构件, 则从外部的远程仓库下载, 并缓存在私服上, 再为客户提供下载服务. 此外, 一些无法从外部仓库下载到的构建也能从本地上传到私服供大家使用(如公司内部二方包、Oracle的JDBC启动等). 为了节省带宽和时间, 一般在公司内部都会架设一台Maven私服, 但将公司内部项目部署到私服还需要对POM做如下配置:

<project >
    ...
    <distributionManagement>
        <repository>
            <id>releases</id>
            <url>http://mvn.server.com/nexus/content/repositories/releases/</url>
        </repository>
        <snapshotRepository>
            <id>snapshots</id>
            <url>http://mvn.server.com/nexus/content/repositories/snapshots/</url>
        </snapshotRepository>
    </distributionManagement>

</project>
发布了49 篇原创文章 · 获赞 15 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/weixin_43164644/article/details/89785540