文章目录
1、Maven核心概念
1.1、坐标
1)数学中的坐标
使用 x、y、z 三个『向量』作为空间的坐标系,可以在『空间』中唯一的定位到一个『点』
2)Maven中的坐标
使用三个『向量』在『Maven的仓库』中唯一的定位到一个『jar』包
- groupId:公司或组织的 id
- artifactId:一个项目或者是项目中的一个模块的 id(模块的名称、工程名)
- version:版本号
1.2、POM
超级POM
- Maven 在构建过程中有很多默认的设定。例如:源文件存放的目录、测试源文件存放的目录、构建输出的目录……等等
- 但是其实这些要素也都是被 Maven 定义过的。定义的位置就是:超级 POM
- 所以我们自己的 POM 即使没有明确指定一个父工程(父 POM),其实也默认继承了超级 POM。就好比一个 Java 类默认继承了 Object 类
<project>
<modelVersion>4.0.0</modelVersion>
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>
<build>
<directory>${project.basedir}/target</directory>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<finalName>${project.artifactId}-${project.version}</finalName>
<testOutputDirectory>${project.build.directory}/test-classes</testOutputDirectory>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<scriptSourceDirectory>${project.basedir}/src/main/scripts</scriptSourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
</testResource>
</testResources>
<pluginManagement>
<!-- NOTE: These plugins will be removed from future versions of the super POM -->
<!-- They are kept for the moment as they are very unlikely to conflict with lifecycle mappings (MNG-4453) -->
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.3</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-5</version>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.8</version>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<reporting>
<outputDirectory>${project.build.directory}/site</outputDirectory>
</reporting>
<profiles>
<!-- NOTE: The release profile will be removed from future versions of the super POM -->
<profile>
<id>release-profile</id>
<activation>
<property>
<name>performRelease</name>
<value>true</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<inherited>true</inherited>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<inherited>true</inherited>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<inherited>true</inherited>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<updateReleaseInfo>true</updateReleaseInfo>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
父 POM
和 Java 类一样,POM 之间其实也是单继承的。如果我们给一个 POM 指定了父 POM,那么继承关系如下图所示:
当前POM
- POM:Project Object Model,项目对象模型
- 当前工程配置的pom配置文件
<!-- 当前Maven工程的坐标 -->
<groupId>com.atguigu.maven</groupId>
<artifactId>pro01-maven-java</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 当前Maven工程的打包方式,可选值有下面三种: -->
<!-- jar:表示这个工程是一个Java工程 -->
<!-- war:表示这个工程是一个Web工程 -->
<!-- pom:表示这个工程是“管理其他工程”的工程 -->
<packaging>jar</packaging>
<name>pro01-maven-java</name>
<url>http://maven.apache.org</url>
<properties>
<!-- 工程构建过程中读取源码时使用的字符集 -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- 当前工程所依赖的jar包 -->
<dependencies>
<!-- 使用dependency配置一个具体的依赖 -->
<dependency>
<!-- 在dependency标签内使用具体的坐标依赖我们需要的一个jar包 -->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<!-- scope标签配置依赖的范围 -->
<scope>test</scope>
</dependency>
</dependencies>
有效 POM
- 在 POM 的继承关系中,子 POM 可以覆盖父 POM 中的配置
- 如果子 POM 没有覆盖,那么父 POM 中的配置将会被继承
- 按照这个规则,继承关系中的所有 POM 叠加到一起,就得到了一个最终生效的 POM
- 执行命令:mvn help:effective-pom
综上所述,平时我们使用和配置的 POM 其实大致是由四个层次组成的:
- 超级 POM:所有 POM 默认继承,只是有直接和间接之分。
- 父 POM:这一层可能没有,可能有一层,也可能有很多层。
- 当前 pom.xml 配置的 POM:我们最多关注和最多使用的一层。
- 有效 POM:隐含的一层,但是实际上真正生效的一层。
1.3、约定的目录结构
2、依赖管理
2.1、依赖范围
- 标签的位置:dependencies/dependency/scope
- 标签的可选值:compile/test/provided/system/runtime/import
main目录 | test目录 | 开发阶段 | 部署到服务器 | |
---|---|---|---|---|
compile(默认) | 有效 | 有效 | 有效 | 有效 |
test | 无效 | 有效 | 有效 | 无效 |
provided | 有效 | 有效 | 有效 | 无效 |
system | 有效 | 有效 | 有效 | 无效 |
runtime | 无效 | 无效 | 无效 | 有效 |
- main目录:在主程序目录是否可以导入此包
- test目录:在测试程序目录是否可以导入此包
- 开发阶段:导入依赖的前提下可以“点”出来
- 部署到服务器:打war包或者可执行jar时候是否添加到lib一起部署到环境上
- compile:通常使用的第三方框架的 jar 包这样在项目实际运行时真正要用到的 jar 包都是以 compile 范围进行依赖的
- test:测试过程中使用的 jar 包,以 test 范围依赖进来。比如 junit
- provided:在开发过程中需要用到,而不需要跟随打包到服务器,因为服务器上有,加上反而可能出现jar冲突。比如servlet-api
- system:此类依赖不是通过maven仓库解析的,而且往往与本级系统绑定,可能造成构建的不可移植,因此应该谨慎使用
<dependency>
<groupId>com.xc.maven</groupId>
<artifactId>xc-maven-test</artifactId>
<version>1.0-SNAPSHOT</version>
<systemPath>D:\temp\xc-maven-test-1.0-SNAPSHOT.jar</systemPath>
<scope>system</scope>
</dependency>
- runtime:在运行的时候依赖,在编译的时候不依赖。典型的例子是jdbc驱动实现
2.2、解决pom单继承问题
<type>pom</type>
用法一:
当一个父pom中的dependencyManagement 标签中需要导入另一个pom中的dependencyManagement的时候,必须同时使用<scope>import</scope> 和 <type>pom</type>
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.1.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- 该pom中dependencyManagement就会包含导入的spring-boot-dependencies中的所有dependencyManagement
- 这是为了解决pom类型的父工程单继承的问题,通过导入,可以导入各种其他父工程的dependencyManagement
- dependencyManagement只在父工程(即pom类型的maven工程)中声明,在子工程中定义无需声明版本从而生效
用法二:
当需要把一些依赖定义到一个pom工程中,但是由于maven单继承机制,子工程又想通过依赖引入该pom工程中的所有依赖,只需要添加<type>pom</type>
<dependencies>
<dependency>
<groupId>org.sonatype.mavenbook</groupId>
<artifactId>persistence-deps</artifactId>
<version>1.0</version>
<type>pom</type>
</dependency>
</dependencies>
- 为了解决子工程单继承的问题,通过<type>pom</type>可以依赖于其他的pom父工程,从而将pom工程中的依赖都传递过来
- type 默认是jar,依赖jar工程时可以不写type标签
2.3、可选依赖
<!--热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
- A依赖上述jar,A可以实现热部署,其实对于A来说,可选依赖没有作用
- 如果有B依赖A,此时热部署依赖是隐藏的,B看不到
- 一句话,阻止依赖传递
2.4、依赖的传递性
传递的原则
在 A 依赖 B,B 依赖 C 的前提下,C 是否能够传递到 A,取决于 B 依赖 C 时使用的依赖范围
- B 依赖 C 时使用 compile 范围:可以传递
- B 依赖 C 时使用 test 或 provided 范围:不能传递
举例:
一个工程,三个模块,install到本地仓库
第一步:project1依赖project2
<dependencies>
<dependency>
<groupId>com.xc</groupId>
<artifactId>project2</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
第二步:project2依赖project3
<dependencies>
<dependency>
<groupId>com.xc</groupId>
<artifactId>project3</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
第三步:查看project1所有的依赖
修改第二步,project2依赖project3的范围
<dependencies>
<dependency>
<groupId>com.xc</groupId>
<artifactId>project3</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
</dependencies>
第三步:查看project1所有的依赖
2.5、版本仲裁
最短路径优先
在下图的例子中,对模块 pro25-module-a 来说,Maven 会采纳 1.2.12 版本
路径相同时先声明者优先
此时 Maven 采纳哪个版本,取决于在 pro29-module-x 中,对 pro30-module-y 和 pro31-module-z 两个模块的依赖哪一个先声明
2.6、依赖的排除
概念
- 当 A 依赖 B,B 依赖 C 而且 C 可以传递到 A 的时候,A 不想要 C,需要在 A 里面把 C 排除掉
- 而往往这种情况都是为了避免 jar 包之间的冲突
配置方式
project1依赖project2,project2依赖proejct3,依赖默认范围compile情况下,project1排查project3
1)前两步如依赖传递的第一步和第二步
2)第三步在A依赖B的时候排除C
<dependency>
<groupId>com.xc</groupId>
<artifactId>project2</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
<!-- 使用excludes标签配置依赖的排除 -->
<exclusions>
<!-- 在exclude标签中配置一个具体的排除 -->
<exclusion>
<!-- 指定要排除的依赖的坐标(不需要写version) -->
<groupId>com.xc</groupId>
<artifactId>project3</artifactId>
</exclusion>
</exclusions>
</dependency>
3)查看project1和project2的所有依赖
- project1间接依赖的project3已移除
- project2对project3的依赖没有变
3、继承
概念
Maven工程之间,A 工程继承 B 工程。本质上是 A 工程的 pom.xml 中的配置继承了 B 工程中 pom.xml 的配置
创建父工程
- 工程名称:pro-maven-parent
- 工程创建好之后,要修改它的打包方式
- 只有打包方式为 pom 的 Maven 工程能够管理其他 Maven 工程
- 打包方式为 pom 的 Maven 工程中不写业务代码,它是专门管理其他 Maven 工程的工程
<groupId>com.xc.maven</groupId>
<artifactId>pro-maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 当前工程作为父工程,它要去管理子工程,所以打包方式必须是 pom -->
<packaging>pom</packaging>
创建子工程
如果子工程坐标中的groupId和version与父工程一致,那么可以省略
<!-- 使用parent标签指定当前工程的父工程 -->
<parent>
<!-- 父工程的坐标 -->
<groupId>com.xc.maven</groupId>
<artifactId>pro-maven-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<!-- 子工程的坐标 -->
<!-- <groupId>com.atguigu.maven</groupId> -->
<artifactId>pro01-maven-module</artifactId>
<!-- <version>1.0-SNAPSHOT</version> -->
父工程中配置依赖的统一管理
被管理的依赖并没有真正被引入到工程
<!-- 使用dependencyManagement标签配置对依赖的管理 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
子工程中引用那些被父工程管理的依赖
- 子工程引用父工程中的依赖信息时,可以把版本号去掉
- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定
<!-- 具体来说是由父工程的dependencyManagement来决定。 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</dependency>
</dependencies>
在父工程中声明自定义属性
通过自定义属性,统一指定Spring的版本
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- 自定义标签,维护Spring版本数据 -->
<my.spring.version>4.3.6.RELEASE</my.spring.version>
</properties>
在需要的地方使用${}的形式来引用自定义的属性名
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${my.spring.version}</version>
</dependency>
4、聚合
使用一个“总工程”将各个“模块工程”汇集起来,作为一个整体对应完整的项目
好处
- 一键执行 Maven 命令:很多构建命令都可以在“总工程”中一键执行
- Maven 要求有父工程时先安装父工程
- 有依赖的工程时,先安装被依赖的工程
- 配置聚合之后,各个模块工程会在总工程中展示一个列表,让项目中的各个模块一目了然
聚合的配置
在总工程中配置 modules 即可
<modules>
<module>pro01-maven-module</module>
<module>pro02-maven-module</module>
<module>pro03-maven-module</module>
</modules>
5、其他核心概念
5.1、生命周期
作用
为了让构建过程自动化完成,Maven 设定了三个生命周期,生命周期中的每一个环节对应构建过程中的一个操作
三个生命周期
生命周期名称 | 作用 | 各个环节 |
---|---|---|
Clean | 清理操作相关 | pre-clean clean post-clean |
Site | 生成站点相关 | pre-site site post-site deploy-site |
Default | 主要构建过程 | validate generate-sources process-sources generate-resources process-resources 复制并处理资源文件,至目标目录,准备打包 compile 编译项目 main 目录下的源代码 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 post-integration-test verify install 将包安装至本地仓库,以让其它项目依赖 deploy 将最终的包复制到远程的仓库,以让其它开发人员共享;或者部署到服务器上运行 |
特点
- 前面三个生命周期彼此是独立的
- 在任何一个生命周期内部,执行任何一个具体环节的操作,都是从本周期最初的位置开始执行,直到指定的地方
5.2、插件和目标
插件
- Maven 的核心程序仅仅负责宏观调度,不做具体工作
- 具体工作都是由 Maven 插件完成的
- 例如:编译就是由 maven-compiler-plugin-3.1.jar 插件来执行的
目标
- 一个插件可以对应多个目标,而每一个目标都和生命周期中的某一个环节对应
- Default 生命周期中有 compile 和 test-compile 两个和编译相关的环节,这两个环节对应 compile 和 test-compile 两个目标,而这两个目标都是由 maven-compiler-plugin-3.1.jar 插件来执行的
5.3、生成微服务可运行 jar 包
- 应用微服务打包插件,可以以 SpringBoot 微服务形式直接运行的 jar 包包括:
- 当前微服务本身代码
- 当前微服务所依赖的 jar 包
- 内置 Tomcat(Servlet 容器)
- 与 jar 包可以通过 java -jar 方式直接启动相关的配置
- 没有如下插件,打包默认只生产依赖jar,不会生产可执行jar
<!-- build 标签:用来配置对构建过程的定制 -->
<build>
<!-- plugins 标签:定制化构建过程中所使用到的插件 -->
<plugins>
<!-- plugin 标签:一个具体插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
举例1
pom文件:
...
<groupId>com.xc</groupId>
<artifactId>springboot-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
...
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
打包结果:
- repackage 功能的作用,分为两步:
- 第一步:首先 mvnpackage 命令 对项目进行打包,打成一个 jar,这个 jar 就是一个普通的 jar,可以被其他项目依赖,但是不可以被执行
- 第二步:然后repackage 命令,对第一步 打包成的 jar 进行再次打包,将之打成一个 可执行 jar ,通过将第一步打成的 jar重命名为 *.original 文件
- springboot-mybatis-1.0-SNAPSHOT.jar
- 命名: artifactId + 版本号,用“-”连接
- 可以使用java -jar xxx直接运行的服务,里面包含依赖的jar
- springboot-mybatis-1.0-SNAPSHOT.jar.original
- 去掉后缀.original则是可以被其他项目依赖的jar
举例2
pom文件:
...
<groupId>com.xc</groupId>
<artifactId>springboot-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
...
<build>
<finalName>my-mybatis</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
</plugins>
</build>
打包结果:
- <finalName>标签:设置打包的名称,不再使用artifactId + 版本号方式
- <classifier>标签:设置可运行服务jar的后缀
- 因为可运行jar有了后缀
- 所有依赖jar就不会重名,也就不用添加后缀.original
5.4、<resources>标签
不写<resources>标签
- 假如pom.xml不加resources
- src/main/java下非java文件都不会编译
(target当中的classes和打出的包解压BOOT-INF当中的classes都找不到) - src/main/resources下的文件都会被编译
(target当中的classes和打出的包解压BOOT-INF当中的classes都可以找到)
<resources>标签只设置src/main/java
- 假如代码中有xml想加载编译,添加如下
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
- 此时src/main/java中的xml会编译,但是src/main/resource中的所有资源都没有加载
- 可以理解为:
- 不加<resource>则默认只加载resource文件
- 添加<resource>则是加哪个路径,只加载哪个路径的文件
最终方案
- <filtering>标签:默认值为false。指定打包时的配置文件中是否进行变量替换
- 如此,java和resources下需要的资源都会加载
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
6、Maven 自定义插件
1、设定打包方式
<packaging>maven-plugin</packaging>
2、引入依赖
核心依赖
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.5.2</version>
</dependency>
使用注解标注执行目标名称
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.5.2</version>
</dependency>
3、创建 Mojo 类
@Mojo(name = "sayHello")//执行目标名称
public class MyHelloPlugin extends AbstractMojo {
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info("---> hello word my maven plugin <---");
}
}
4、配置build
<build>
<plugins>
<plugin>
<groupId>com.xc</groupId>
<artifactId>hello-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<executions>
<execution>
<!-- 指定唯一标识 -->
<id>hello1</id>
<!-- 指定和目标关联的生命周期阶段 -->
<phase>validate</phase>
<!-- 关联指定生命周期的目标 -->
<goals>
<!-- goals 标签中可以配置多个 goal 标签,表示一个生命周期环节可以对应当前插件的多个目标 -->
<goal>sayHello</goal>
</goals>
</execution>
<execution>
<id>hello2</id>
<phase>compile</phase>
<goals>
<goal>sayHello</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
5、执行compile,绑定两个周期,执行两次
6、插件识别方式
前置匹配:
- 匹配规则:${prefix}-maven-plugin
- artifactId:hello-maven-plugin
- 前缀hello,插件名为hello
中间匹配:
- 匹配规则:maven-${prefix}-plugin
- artifactId:maven-good-plugin
- 前缀:good
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/148610.html