用了maven两三年了,都是拿过来直接用的,今天来细细讲解一下maven的各种参数:
一、介绍
Maven是基于项目对象模型(POM project object model),可以通过一小段描述信息(配置)来管理项目的构建,报告和文档的软件项目管理工具,简单的说就是用来管理项目所需要的依赖且管理项目构建的工具。
二、maven作用
Maven 官网:https://maven.apache.org
- 在开发中,为了保证编译通过,我们会到处去寻找jar包,当编译通过了,运行的时候,却发现"ClassNotFoundException",我们想到的是,难道还差jar包?
- 每个Java项目的目录结构都没有一个统一的标准,配置文件到处都是,单元测试代码到底应该放在那里也没有一个权威的规范。
- 因此,我们就要用到Maven(使用Ant也可以,不过编写Ant的xml脚本比较麻烦)----一个项目管理工具。
- Maven主要做了两件事:
统一开发规范与工具
统一管理jar包
三、maven的生命周期
maven把项目的构建划分为不同的生命周期(lifecycle)。包括:
- 验证(validate):验证项目是否正确
- 编译(compile):编译项目的源代码
- 测试(test):使用合适的单元测试框架测试编译的源代码。这些测试不应该要求代码被打包或部署
- 打包(package):项目打包成war包或jar包
- 验证(verify)
- 安装(install):打包后的jar包,安装要本地的repository中。
- 部署(deploy)
maven中所有的执行动作(goal)都需要指明自己在这个过程中的执行位置,然后maven执行的时候,就依照过程的发展一次调用这些goal进行各种处理。
这个也是maven的一个基本调度机制。一般来说,位置稍后的过程都会依赖于之前的过程。当然,maven同样提供了配置文件,可以依照用户要求,跳过某些阶段。
三、maven规范
maven使用如下几个要素来唯一定位某一个输出物:
groupId;artifactId;packaging;version。
- groupId 公司、团体等。比如Apache Software的项目有以下org。apache开头的groupId。
- artifactId项目是唯一标识符。比如一个helloworld项目就叫helloworld。
- packaging项目打包输出的类型,默认是jar包,也可以为war包,产生一个web应用。
- version一个项目的特定的版本。发布的项目有一个固定的版本表示来指向该项目的某一个特定的版本。而正在开发中的项目可以用一个特殊的标识,这种标识个版本加上一个“SNAPSHOT”的标记。
maven在版本管理时候可以使用几个特殊的字符串 SNAPSHOT ,LATEST ,RELEASE 。比如"1.0-SNAPSHOT"。各个部分的含义和处理逻辑如下说明:
1. SNAPSHOT 如果一个版本包含字符串"SNAPSHOT",Maven就会在安装或发布这个组件的时候将该符号展开为一个日期和时间值,转换为UTC时间。例如,"1.0-SNAPSHOT"会在2010年5月5日下午2点10分发布时候变成1.0-20100505-141000-1。这个词只能用于开发过程中,因为一般来说,项目组都会频繁发布一些版本,最后实际发布的时候,会在这些snapshot版本中寻找一个稳定的,用于正式发布,比如1.4版本发布之前,就会有一系列的1.4-SNAPSHOT,而实际发布的1.4,也是从中拿出来的一个稳定版。
2.LATEST 指某个特定构件的最新发布,这个发布可能是一个发布版,也可能是一个snapshot版,具体看哪个时间最后。
3.RELEASE 指最后一个发布版
四、maven项目依赖
1.多模块依赖和继承
项目结构图:
parent
├─childA(model层)
│ └─pom.xml(jar)
├─childB(web层)
│ └─pom.xml(war)
└─pom.xml(pom)
- parent中执行mvn install就能将 childA和childB 一起编译
parent的pom.xml做如下配置:
<groupId>com.ht</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging> <!-- pom表示它是一个被继承的模块 -->
<modules>
<module>childA</module> <!-- 不加module则不会被联合编译 -->
<module>childB</module>
</modules>
childA和childB的pom.xml都需要配置parent,防止引入的包冲突(如果不加parent,会分别去编译他们引入的依赖,会重复引入包):
<!-- childA 的 pom.xml-->
<parent>
<artifactId>parent</artifactId>
<groupId>com.ht</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>childA</artifactId>
<packaging>jar</packaging>
<!-- childB 的 pom.xml-->
<parent>
<artifactId>parent</artifactId>
<groupId>com.ht</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>childB</artifactId>
<packaging>war</packaging>
- 子pom间存在引用关系,比如childB引用到了childA的jar包
<dependency>
<groupId>com.module</groupId>
<artifactId>childA</artifactId> <!--加上childA的依赖-->
<version>1.0-SNAPSHOT</version>
</dependency>
2.子项目继承父项目的依赖
parent中加上dependencyManagement,child项目就可以继承parent项目的依赖,并且在child中可以不用加version了。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
子项目直接引入parent就行
3.依赖范围
dependency依赖关系中Scope(范围)的作用:主要管理依赖的部署。
如果不显示执行 <scope>
删除线格式 属性时,默认 <scope>compile</scope>
。
scope 属性包括(5个值):
1. compile(编译范围):编译范围的<dependency>在所有的classpath中可用,同时它们也会被打包
2. provided(已提供范围):表示部署的环境当中有某容器已经提供了该jar包,只在编译classpath(不是运行时)可用。它们不是传递性的,也不会被打包。例如,如果你开发了一个web应用,你可能在编译classpath中需要可用的Servlet API来编译一个servlet,但是你不会想要在打包好的WAR中包含这个Servlet API;这个Servlet API JAR由你的servlet容器(Tomcat)提供。
3. runtime(运行时范围):只在运行和测试系统的时候需要,但在编译的时候不需要。
4. test(测试范围):在一般的 编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用。
5. system (系统范围):该范围不推荐使用(你应该一直尽量去从公共或定制的Maven仓库中引用依赖)
详细可参考:https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope
4. 可选依赖(Optional Dependencies)和依赖排除(Dependency Exclusions)
maven的依赖关系是有传递性的。如:A–>B,B–>C。但有时候,项目A可能不是必需依赖C,因此需要在项目A中排除对A的依赖。在maven的依赖管理中,有两种方式可以对依赖关系进行,分别是可选依赖(Optional Dependencies)以及依赖排除(Dependency Exclusions)。
4.1 可选依赖 optional
当一个项目A依赖另一个项目B时,项目A可能很少一部分功能用到了项目B,此时就可以在A中配置对B的可选依赖。举例来说,一个类似hibernate的项目,它支持对mysql、oracle等各种数据库的支持,但是在引用这个项目时,我们可能只用到其对mysql的支持,此时就可以在这个项目中配置可选依赖。
配置可选依赖的原因:1、节约磁盘、内存等空间;2、避免license许可问题;3、避免类路径问题,等等
。
<dependency>
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
<version>1.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
假设以上配置是项目A的配置,即:Project-A --> Project-B。在编译项目A时,是可以正常通过的。
如果有一个新的项目X依赖A,即:Project-X -> Project-A。此时项目X就不会依赖项目B了。如果项目X用到了涉及项目B的功能,那么就需要在pom.xml中重新配置对项目B的依赖。
4.2 依赖排除 (exclusions)
当一个项目A依赖项目B,而项目B同时依赖项目C,如果项目A中因为各种原因不想引用项目C,在配置项目B的依赖时,可以排除对C的依赖。
示例(假设配置的是A的pom.xml,依赖关系为:A --> B; B --> C):
<dependency>
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
<version>1.0</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>sample.ProjectC</groupId>
<artifactId>Project-C</artifactId>
</exclusion>
</exclusions>
</dependency>
当然,对于多重依赖,配置也很简单,参考如下示例:
项目A–> 项目B --> 项目C --> 项目D --> 项目E
<dependency>
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>sample.ProjectE</groupId>
<artifactId>Project-E</artifactId>
</exclusion>
</exclusions>
</dependency>
5. dependency中的type属性
dependency中type默认为jar即引入一个特定的jar包。那么为什么还会有type为pom呢?
当我们需要引入很多jar包的时候会导致pom.xml过大,我们可以想到的一种解决方案是定义一个父项目,但是父项目只有一个,也有可能导致父项目的pom.xml文件过大。这个时候我们引进来一个type为pom,意味着我们可以将所有的jar包打包成一个pom,然后我们依赖了pom,即可以下载下来所有依赖的jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
6.properties标签
用户可以使用该类属性引用POM文件中对应元素的值。如${project.artifactId}就对应了<project>
<artifactId>
元素的值,常用的POM属性包括:
${project.build.sourceDirectory}:项目的主源码目录,默认为src/main/java/
${project.build.testSourceDirectory}:项目的测试源码目录,默认为src/test/java/
${project.build.directory} : 项目构建输出目录,默认为target/
${project.outputDirectory} : 项目主代码编译输出目录,默认为target/classes/
${project.testOutputDirectory}:项目测试主代码输出目录,默认为target/testclasses/
${project.groupId}:项目的groupId
${project.artifactId}:项目的artifactId
p r o j e c t . v e r s i o n : 项 目 的 v e r s i o n , 与 {project.version}:项目的version,与 project.version:项目的version,与{version} 等价
p r o j e c t . b u i l d . f i n a l N a m e : 项 目 打 包 输 出 文 件 的 名 称 , 默 认 为 {project.build.finalName}:项目打包输出文件的名称,默认为 project.build.finalName:项目打包输出文件的名称,默认为{project.artifactId}-${project.version
例子:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>2.1.6_RELEASE</spring.version>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<developer.organization><![CDATA[xy公司]]></developer.organization>
</properties>
五、Maven插件机制
1. Maven默认插件
不配置Plugin时,Maven默认会使用以下插件。如果针对各个 plugin 有特殊配置的话,需要显示指定 plugin 和 属性配置。
其中的build标签描述了如何来编译及打包项目,而具体的编译和打包工作是通过build中配置的 plugin 来完成。当然plugin配置不是必须的,默认情况下,Maven 会绑定以下几个插件来完成基本操作。
plugin | function | life cycle phase |
---|---|---|
maven-clean-plugin | 清理上一次执行创建的target文件 | clean |
maven-resources-plugin | 处理资源文件 | resources,testResources |
maven-compiler-plugin | 编译Java代码 | compile、testCompile |
maven-surefire-plugin | 执行单元测试文件 | test |
maven-jar-plugin | 创建 jar | package |
maven-install-plugin | 清理上一次执行创建的target文件拷贝jar到本地的maven仓库 /repository 下面清理上一次执行创建的target文件 | install |
maven-deploy-plugin | 发布 ja | deploy |
maven-site-plugin | 生成文档 | site |
maven-site-plugin:将工程所有文档生成网站,生成的网站界面默认和apache的项目站点类似,但是其文档用doxia格式写的,目前不支持docbook,需要用其他插件配合才能支持。需要指出的是,在maven 2.x系列中和maven3.x的site命令处理是不同的,在旧版本中,用 mvn site 命令可以生成reporting节点中的所有报表,但是在maven3中,reporting过时了,要把这些内容作为 maven-site-plugin的configuration的内容才行。
<build>
<!-- resources 是 maven-resources-plugin 的-->
<resources>
<resource>
<directory>src/main/java</directory>
<filtering>true</filtering> <!-- filtering 用来表示资源文件中的占位符是否需要被profiles中的属性动态替换,true为需要替换。 -->
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.properties</include>
<include>*.xml</include>
<include>*.dic</include>
<include>*.txt</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>*.p12</include>
</includes>
</resource>
</resources>
<plugins>
<!--
maven-compiler-plugin编译源代码。
指定maven编译的jdk版本,如果不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3
windows默认使用GBK编码,java项目经常编码为utf8,也需要在compiler插件中指出,否则中文乱码可能会出现编译错
-->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!--
maven-resources-plugin用来处理资源文件,默认的主资源文件目录是src/main/resources。
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
2.maven常用的插件
spring-boot-maven-plugin
当使用SpringBoot开发项目的时候,会使用到spring-boot-maven-plugin插件
Spring Boot Maven plugin有5个Goals:
命令 | 说明 |
---|---|
spring-boot:repackage | 默认goal。在mvn package之后,再次打包可执行的jar/war, 并将mvn package生成的软件包重命名为*.original |
spring-boot:run | 运行Spring Boot应用 |
spring-boot:start | 在mvn integration-test阶段,进行Spring Boot应用生命周期的管理 |
spring-boot:stop | 在mvn integration-test阶段,进行Spring Boot应用生命周期的管理 |
spring-boot:build-info | 生成Actuator使用的构建信息文件build-info.properties |
其中比较重要的命令是:
mvn package spring-boot:repackage
执行后会看到生成的两个jar文件,一个是*.jar,另一个是*.jar.original。
这是由于在执行上述命令的过程中,Maven首先在package阶段打包生成*.jar文件;然后执行spring-boot:repackage重新打包
2.1 maven-source-plugin
maven-source-plugin提供项目自动将源码打包并发布的功能,在需要发布源码项目的pom.xml文件中添加如下代码即可:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<configuration>
</configuration>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
执行 mvn install
,maven会自动将source install到repository 。
执行 mvn deploy
,maven会自动将source deploy到remote-repository 。
执行 mvn source:jar
,单独打包源码。
注意:在多项目构建中,将source-plugin置于顶层或parent的pom中并不会发挥作用,必须置于具体项目的pom中。
至此,参数不全,后续再补!