Maven

ANT工具帮助我们编译、打包项目。Apache在ANT的基础上升级,研发出自动化构建工具Maven。

Maven基于对象模型POM的概念,即Project Object Model。通过描述信息来管理项目的构建,包括对项目进行编译、测试、打包、部署、上传到私服等。在Maven中每个项目都是一个对象,对象之间的关系包括依赖、继承、聚合,更方便地实现导入jar包,拆分项目等效果。

Maven解决了jar包的复杂依赖管理。提供Maven中央仓库。

另外实现项目的多模块管理。

一篇文章学懂Maven

Maven的坐标

通过groupId、artifactId和version定位一个Maven工程。

Maven的依赖

具有传递性。

依赖范围决定了依赖的坐标在什么情况下有效或无效。

compile

默认范围,表示在编译和运行都有效。

provided

典型的例子是servlet-api,在编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复引入一遍。

runtime

编译时不需要生效,只在运行时生效。典型例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。

system

范围同provided类似,不过必须通过systemPath元素显示指定一个本地系统路径的JAR。

test

只在编译测试代码和运行测试的时候需要,应用的正常运行不需要此类依赖。典型例子是JUnit。

Import

只适用于pom文件的<dependencyManagement>的部分。表明指定的POM必须使用<dependencyManagement>部分的依赖。

父工程可以通过pom.xml配置import属性结合dependencyManagement,强制规范子工程项目版本。

注意:在父项目中放在<dependencyManagement>中的内容不被子项目继承,不可以直接使用。

放在<dependencyManagement>中的内容主要目的是进行版本管理。里面的内容在子项目中依赖时坐标只需要填写<groupId>和<artifactId>即可。

如果子项目不希望使用父项目的版本,可以明确配置version。

多模块聚合

当工程拥有2个以上模块的时候,每个模块都是一个独立的功能集合。开发的时候每个模块都可以独立编译、测试、运行。

这个时候我们需要一个聚合工程。该聚合工程必须是一个POM类型的工程(Maven Project),各子模块可以是任意类型的模块(Maven Module)。

<packaging>pom</packaging>

聚合时多个项目的本质还是一个项目,多个项目被一个大的父项目包含。同时在父项目的pom.xml中的<modules>表示包含的所有子项目。


Maven命令

clean清理

用于清除Maven项目中的target目录,以便重新构建项目。

validate验证

验证项目是否正确并且所有必要的信息均可用。

compile编译源代码

编译项目的源代码,主要是java文件。

一般是编译src/main/java或是src/test/java里面的文件

test运行测试

使用合适的单元测试框架来测试compile中编译得到的源代码。这些测试不应要求将代码打包或者部署。

package打包

生成target目录,完成项目的编译、单元测试、打包,但没有把打好的可执行jar包(war包或者其他形式的包)部署到本地maven仓库和远程maven私服仓库。

verify验证

用来验证test,检查test的结果是否满足标准。

install安装至本地仓库

完成项目的编译、单元测试、打包,而且把打好的可执行jar包(war包或者其他形式的包)部署到本地maven仓库,但没有部署到远程maven私服仓库。

将Maven项目打包并安装到本地Maven仓库中,以便其他项目可以引用该项目。

在本地Respository中安装jar。

site

用于为Maven项目生成站点(用以生成HTML页面的模块等文档)。

deploy复制到远程仓库

完成项目的编译、单元测试、打包,而且把打好的可执行jar包(war包或者其他形式的包)部署到本地maven仓库和远程maven私服仓库。

共享给其他开发人员和项目。

mvn compile 编译类文件

mvn clean package 依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)等7个阶段。

mvn clean install 依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)、install等8个阶段。

mvn clean deploy 依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)、install、deploy等9个阶段。

mvn -version/-v 显示版本信息

mvn -e 显示详细错误信息

mvn dependency:tree 打印整个依赖树

Maven生命周期lifecycle

clean生命周期

pre-clean:执行一些清理前需要完成的工作
clean:清理上一次构建生成的文件
post-clean:执行一些清理后需要完成的工作

default生命周期

default生命周期定义了真正构建项目中需要执行的所有步骤,它包含的阶段如下:

validate
initialize
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-classed
test:使用单元测试框架运行测试,测试代码不会被打包或部署
prepare-package
package:接受编译好的代码,打包成可发布的格式,jar/war等
pre-integration-test
integration-test
post-integration-test
verify
install
deploy

site生命周期

maven生命周期lifecycle和plugins介绍_lifecycle和plugins区别_ximeneschen的博客-CSDN博客

maven-之Lifecycle详解_mvn lifecycle_拉面牛奶的博客-CSDN博客

Maven插件plugin

生命周期的各个阶段都是抽象的概念,真正干活的是一个个的插件。插件是以独立的构件形式存在,我们将maven的生命周期的各个阶段与maven的插件进行绑定,当我们执行mvn命令其实就是在指挥着一个个的插件在干活。

插件的目标

maven的插件以独立的构件形式存在,为了能够复用代码,使得一个插件可以完成多个任务,我们定义了插件目标(Plugin Goal),每一个目标都可以完成不同的功能。

  • maven-dependency-plugin插件具有多个功能,比如分析项目依赖,还能列出项目的依赖树等。就是使用了analyze, tree和list等插件目标区分的。

dependency : analyze
dependency : tree
denpendency : list

插件的绑定

内置绑定

maven的生命周期的各个阶段与maven插件互相绑定,从而完成具体的构件任务。default生命周期的compile阶段与maven-compiler-plugin插件的目标compile进行绑定的示意图如下所示:

为了能够让用户更加方便的构建项目,maven将大多数主要的生命周期阶段都绑定了很多插件的目标。如下所示:

<build><plugins></plugins></build>

定义构建过程中用到的插件,比如spring-boot-maven-plugin,sonar-maven-plugin。

由上图可知,各个插件的执行顺序一般是:

1:clean、2:resources、3:compile、4:testResources、5:testCompile、6:test、7:jar、8:install。

在图中标记的地方每一行都是由冒号分隔的,前半部分是对应的插件,后半部分是插件的执行目标也就是插件执行产生的结果。

现在我们来看下上面的pom文件,我们如配置了maven-compiler-plugin这个插件,其它的插件没有配置,但最后项目构建成功,说明maven内置了各种插件。如果pom中没有配置就调用默认的内置插件,如果pom中配置了就调用配置的插件。

到此我们理解maven的构建过程或者有更多的人称是打包,就是由各种插件按照一定的顺序执行来完成项目的编译,单元测试、打包、布署的完成。各种插件的执行过程也就构成的maven的生命周期(lifecycle)。

生命周期(lifecycle)各个阶段并非不能独立的,可以单独执行如mvn clean,也可以一起执行如mvn clean install。而且有的mvn命令其是包括多个阶段的,如mvn compile其是包括了resources和compile两个阶段。

下面分别来分析各个阶段需要的插件和输出的结果


clean插件maven-clean-plugin

clean阶段是独立的一个阶段,功能就是清除工程目前下的target目录,对应的插件是 maven-clean-plugin,可以使用maven内置的插件,当然也可以自己在pom中配置。下面看下mvn执行前后工程目录下的输出对比

resources插件maven-resources-plugin

resource插件的功能就是把项目需要的配置文件拷贝到指定的目录,默认是拷贝src\main\resources目录下的文件到classes目录下,当然可以自己来配置源目录和输出目录。resources插件一般不单独执行,compile插件执行时会先调用resources插件。配置示例如下:

<plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-resources-plugin</artifactId>  
         <version>x.x</version>  
         <executions>  
           <execution>  
              <id>copy-resources</id>  
              <!-- 在default生命周期的 validate阶段就执行resources插件的copy-resources目标 -->  
              <phase>validate</phase>  
              <goals>  
                <goal>copy-resources</goal>  
              </goals>  
              <configuration>  
              <!-- 指定resources插件处理资源文件到哪个目录下 -->  
                 <outputDirectory>${project.build.outputDirectory}</outputDirectory>  
                  <!-- 也可以用下面这样的方式(指定相对url的方式指定outputDirectory) <outputDirectory>target/classes</outputDirectory> -->  
                  <!-- 待处理的资源定义 -->  
                  <resources>  
                     <resource>  
                        <!-- 指定resources插件处理哪个目录下的资源文件 -->  
                        <directory>src/main/${deploy.env}/applicationContext.xml</directory>  
                        <!-- 指定不需要处理的资源 <excludes> <exclude>WEB-INF/*.*</exclude> </excludes> -->  
                        <!-- 是否对待处理的资源开启过滤模式 (resources插件的copy-resources目标也有资源过滤的功能,这里配置的   
                        这个功能的效果跟<build><resources><resource>下配置的资源过滤是一样的,只不过可能执行的阶段不一样, 这里执行的阶段是插件指定的validate阶段,<build><resources><resource>下的配置将是在resources插件的resources目标执行时起作用(在process-resources阶段)) -->  
                        <filtering>false</filtering>  
                     </resource>  
                  </resources>  
              </configuration>  
              <inherited></inherited>  
           </execution>  
         </executions>  
         </plugin>  

compile插件maven-compiler-plugin

compile插件执行时先调用resources插件,功能就是把src\main\java源码编译成字节码生成class文件,并把编译好的class文件输出到target\classes目录下。下面看执行结果:



单元测试所用插件

单元测试所用的compile和resources插件和主代码是相同的,但执行的目标不同,目标testCompile和testResources是把src\test\java下的代码编译成字节码输出到target\test-classes,同时把src\test\resources下的配置文件拷贝到target\test-classes。看下面的输出:


插件maven-surefire-plugin执行单元测试类

在本例中就是运行contextLoads()方法,如果单元测试不通过,构建会失败,在编译正式的项目时可以使用mvn -Dmaven.test.skip=true 来跳过测试类的编译和运行过程。mvn test可以单独执行,但是这个命令其实是包括了resources、compile、testResources、testCompile、test这几个阶段,如下图所示:


打包插件

这个插件是把class文件、配置文件打成一个jar(war或其它格式)包。依赖包是不在jar里面的,需要建立lib目录,且jar和lib目录在同级目录。常用的打包插件有maven-jar-plugin、maven-assembly-plugin、maven-shade-plugin三种,下面分别介绍下各自pom配置和使用特点。

- maven-jar-plugin

可执行jar与依赖包是分开,需建立lib目录里来存放需要的j依赖包,且需要jar和lib目录在同级目录

<plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
                 <version>2.6</version>
                 <configuration>
                    <archive>
                         <manifest>
                             <addClasspath>true</addClasspath>
                             <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>com.xxx.xxxService</mainClass>
                       </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>x.x</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>


maven-assembly-plugin

这个插件可以把所有的依赖包打入到可执行jar包。但是该插件有个bug会缺失spring的xds文件,导致无法运行jar,同时如果同级目录还有其它可执行jar文件依赖可能会产生冲突。

<plugin>
                 <artifactId>maven-assembly-plugin</artifactId>
                 <configuration>
                     <descriptorRefs>
                         <descriptorRef>jar-with-dependencies</descriptorRef>
                     </descriptorRefs>
                     <archive>
                         <manifest>
                             <mainClass>com.xxx.xxxService</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>


maven-shade-plugin

所有的依赖包打入到可执行jar包,如果同级目录有其它可执行jar,依赖可能会产生冲突,且运行jar时,有时会出现SF、DSA、RSA文件冲突的提示,需要排除META-INF目录下的文件。

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>x.x.x</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/*.SF</exclude>
                                        <exclude>META-INF/*.DSA</exclude>
                                        <exclude>META-INF/*.RSA</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                            <transformers>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.handlers</resource>
                                </transformer>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.schemas</resource>
                                </transformer>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                    <resource>META-INF/spring.tooling</resource>
                                </transformer>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.xxx.xxxInvoke</mainClass>
                                </transformer>
                            </transformers>
                            <minimizeJar>true</minimizeJar>
                            <shadedArtifactAttached>true</shadedArtifactAttached>
                        </configuration>
                    </execution>
                </executions>
            </plugin>


maven-install-plugin

发布插件的功能就是把构建好的artifact部署到本地仓库,还有一个deploy插件是将构建好的artifact部署到远程仓库。

自定义绑定

maven为了满足用户多元化的构建过程,是允许我们自定义的选择将某个插件绑定到生命周期的某个阶段的。接下来,我们将maven-source-plugin插件的jar-no-fork任务绑定到verify生命周期阶段,我们只需要在pom中增加如下的配置即可:

<build>
    <plugins>
        <plugin>
            <artifactId>maven-source-plugin</artifactId> // 插件
            <version>3.0.1</version>
            <executions>                             // 可以配置执行的任务
                <execution>
                    <id>attach-sources</id>     // 任务id
                    <phase>verify</phase>      // 生命周期verify阶段
                    <goals>
                        <goal>jar-no-fork</goal> // 插件目标
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

执行:mvn verify

插件的配置

在项目的配置文件pom中,我们可以对插件进行全局的配置,举例如下:

<plugins> 
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.4</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <fork>true</fork>
                    <verbose>true</verbose>
                    <encoding>UTF-8</encoding>
                    <compilerArguments>
                        <sourcepath>
                            ${project.basedir}/src/main/java
                        </sourcepath>
                    </compilerArguments>
                    <compilerArgument>-XDignore.symbol.file</compilerArgument>
                </configuration>
            </plugin>
</plugins> 
常用maven插件

maven-assembly-plugin :自定义打包方式

maven-enforcer-plugin :展示项目依赖冲突

maven-antrun-plugin :运行ant脚本

maven-dependency-plugin :分析项目依赖

maven-clean-plugin :清理

maven-compiler-plugin :编译

maven-source-plugin :源码

maven-war-plugin :打包

maven-jar-plugin:打包

maven-surefire-plugin :测试

maven-resources-plugin :资源文件处理

Parent和dependency的区别

比如项目A在其pom中引用了依赖包X,并定义了类Y。

项目B用parent方式引用了项目A,则项目B中可以直接使用项目A中引入的依赖X,不可以使用类Y。

项目C用dependency方式引用了项目A,则项目C中可以直接使用项目A中引入的依赖X,也可以使用类Y。

 注意:这就是为什么我在自己的微服务框架中,各项目通过dependency方式引用了项目txw-framework-starter。

DependencyManagement的作用

DependencyManagement只是声明依赖,不实现引入。

比如在父项目的pom的DependencyManagement声明了依赖X,在子项目中不会自动引入,需要在子项目的pom中声明依赖X。

 

如果在同一个pom中,DependencyManagement和dependency都声明了依赖X,则以子项目的dependency优先。

如果在不同的pom中,比如存在父子关系的pom中,两者都声明了依赖X,如果子项目的pom的依赖X的声明没有指定version和scope,则自动继承父项目的version和scope。如果子项目的pom的依赖X的声明已经指定version和scope,则以子项目的为准。

 

通常应用于多个子项目的工程,在父项目的DependencyManagement指定所有子项目的公共依赖包的版本。这样,所有子项目依赖的公共依赖包都保持一致,统一了模块之间依赖的版本。如果某一子项目需要不同版本的公共依赖包,可以在自己的pom中自定义版本。

 

Parent的作用

Parent用于声明项目之间的父子继承关系,用于管理多个项目之间公共的依赖项:多个项目有共同的依赖,而且依赖项的版本发生变更时,需要所有项目同步更新。

 

使用方式一、定义一个parent项目A,在A的pom中通过<dependency>声明依赖项P。

在子项目B和C的pom中声明parent是A,即可实现子项目B和C对依赖项P的依赖,以及版本的统一管理。

 

使用方式二、定义一个parent项目A,在A的pom中通过<dependencyManagement>声明依赖项P。

在子项目B和C的pom中通过<parent>声明parent是A,并通过<dependency>声明依赖项P(无需声明依赖的版本),即可实现B和C对依赖项P的依赖,以及版本的统一管理。

同时,在子项目D的pom中通过<parent>声明parent是A,但是不通过<dependency>声明依赖项P,即表示项目D并不依赖P。

Maven自定义插件开发

Maven插件开发-CSDN博客

Maven常见问题

程序包不存在

原因:编译生成的class文件放在BOOT-INF目录下。

解决:修改Maven插件,增加配置项。

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <skip>true</skip>
    </configuration>
    <version>2.7.10</version>
</plugin>