Maven 包管理工具
- JVM 世界里面只有全限定类名(Full Qualified Class Name),类的全限定类名唯一确定了一个类
- JVM 的工作 => 执行一个类的字节码,假如这个过程中碰到了新的类,加载它
- 类路径(Classpath) => JVM 在 classpath 里面寻找类
- 包就是把许多类放在一起打的压缩包
- 传递性依赖 => 你依赖的类还依赖了别的类
- Classpath Hell => 当多个同名类同时出现在 Classpath 中,就是噩梦的开始
- 包管理 => 告知 JVM 如何找到所需的第三方类库以及成功地解决其中的冲突问题
Maven 包管理
- Maven 在包管理所做的事情 => 查看坐标,根据坐标去中央仓库中定位包,之后查看 pom 文件,找到依赖的包,之后逐个查看坐标,定位包,将所有的包都下载到本地,之后将这些依赖都放置在 classpath 中,供 JVM 去检索,之后启动 JVM
- 传递性依赖原则 => 决不允许最终 classpath 出现同名不同版本的 jar 包
- 自动管理传递性依赖
- 自动解决冲突
坐标系统
- 通过唯一的坐标定位包 => groupId + artifactId + version
- 坐标可读性好、简单方便
- 语义化版本,方便自动化工具处理
- 一旦发布不可修改,实现稳定的构建
元数据系统
- jar 包本身是没有元信息,很难知道依赖谁
- pom.xml 存储这个 jar 包的传递性依赖关系
scope
pom 文件中的 <dependency>
标签中的 <scope>
标签
- compile => 生产代码的依赖
- provided => 编译的时候需要该依赖,运行的时候是由外部容器提供了相关依赖 e.g. Servlet API
- test => 测试相关依赖 e.g. JUnit
- runtime => 只有在运行的时候需要的依赖 e.g. mysql-connector等JDBC的包
构建的4个步骤,其中每个步骤都需要一个 classpath 去进行相应的工作
- javac 编译 main 目录 => compile + provided
- javac 编译 test 目录 => compile + provided + test
- java 运行所有测试 => compile + test + runtime
- java 运行生产代码 => compile + runtime
冲突解决
当看到如下异常的时候,意味着包冲突出现了
- AbstractMethodError
- NoClassDefFoundError
- ClassNotFoundException
- LinkageError
Maven 解决冲突原则
- 最近原则
- 最先声明原则
Maven 中央仓库
- 按照一定的约定存储 pom、jar(class文件)、javadoc(HTML文件)、resources(源代码文件)
- 依赖包的解析与检索 => https://repo1.maven.org/maven2/com/google/guava/guava/ => https://repo1.maven.org/maven2/<groupId>/<artifactId>/<version>
<groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>28.0-jre</version>
- 中央仓库的声明与镜像
- 中央仓库、快照仓库和插件仓库
- 中央仓库 =>
<repositories></repositories>
- 快照仓库 =>
- 插件仓库 =>
<pluginRepositories></pluginRepositories>
- 中央仓库 =>
- 快照版本与更新 =>
<version>0.1-SNAPSHOT</vesion>
=> 快照版本可以重复更新 => 主要解决在开发过程中多方同时引用同一个 jar 包,该 jar 包需要频繁更新 => 并不是每次运行mvn clean verify
时该 jar 包都从仓库里面拉取最新的,默认是每天更新一次 =>mvn clean verify -U
强行重新解析 SNAPSHOT
Maven 本地仓库
~/.m2
- 中央仓库的本地缓存 => 先找本地仓库,否则就去中央仓库找,找到了就下载到本地仓库
- 启动 java/javac 命令时的包来源
- 跨项目/模块开发时的代码共享
发布包
希望同一份代码在本地、团队、公司甚至时间范围内共享
- 本地 =>
mvn install
- 团队、公司 =>
mvn deploy
私服 - 世界 =>
mvn deploy
中央仓库
发布的本质 => 将 pom/jar 按照仓库约定推送到服务器上
Maven 自动化构建工具
默认的三套生命周期
- default => validate -> initialize -> generate-sources -> precess-sources -> generate-resources -> precess-resources ->
compile -> precess-classes -> generate-test-sources -> process-test-sources -> generate-test-resources ->
process-test-resources -> test-compile -> precess-test-classes -> test -> prepare-package -> package ->
pre-integration-test -> integration-test -> post-integration-test -> verify -> install -> deploy - clean => 删除 target
- site
![](https://img.haomeiwen.com/i9617841/ac7fab013faafe74.png)
Maven Plugin
- 插件是 Maven 的灵魂,实际上就是一组代码
- 插件可以声明多个目标(Goal),称为 MOJO(Maven Old Java Object)
- 插件的
<groupId>
是可以忽略的,默认是<groupId>org.apache.maven.plugins</groupId>
- 同一个目标可以绑定到多个阶段上,执行多次
- 默认绑定 => 不需要手工进行,由插件声明
- 从命令行直接调用插件 =>
- mvn dependency:tree
- mvn surefire:test => https://repo1.maven.org/maven2/org/apache/maven/plugins/maven-metadata.xml =>
<prefix>
- mvn flyway:migrate => https://repo1.maven.org/maven2/org/flywaydb/maven-metadata.xml =>
<prefix>
prefix
- mvn help:describe -Dplugin=org.flywaydb:flyway-maven-plugin
- 在 $PATH 环境变量中找 mvn 可执行程序,找到之后将后面的参数传递给 maven
- maven 启动 JVM,通过 System property 传递给 JVM, System property key 为 plugin, value 为 org.flywaydb:flyway-maven-plugin
- maven 查找 help 的 plugin,执行 help plugin 的 describe 目标
-
help plugin 的 describe 目标获取 key 为 plugin 的 System property,之后执行相应的逻辑
help:describe
插件解析
- groupId + artifactId + version 去插件仓库中寻找
- 如果 groupId 不指定,默认是 org.apache.maven.plugins
- 如果 version 不指定,则可从父 POM 继承
- 如果 groupId/artifactId 不指定,可按照 prefix 指定
编写插件
- pom 文件中的打包方式是 maven-plugin
- 最终代码会被打包成为 Maven 可以识别的 jar 包
- 使用 archetype 生成项目结构 =>
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-plugin -DarchetypeVersion=1.4
- 目标 <=> MOJO
- MOJO 与默认阶段
- 注入 MOJO 运行所需的属性
- 执行具体的业务逻辑 =>
public void execute() throws MojoExecutionException
Maven 私服
- Hosted => 用于存放私有的包
- Proxy => 用于代理其他仓库,提高速度,节约资源
nexus
- nexus docker
- Google search => maven deploy to nexus
artifactory
Maven 多模块
- 软件工程的原则 => 高内聚低耦合
- 模块化的系统 => 用户自由选择所需模块
- 可能带来性能的提升
- 多模块 -> 独立的项目 -> 微服务
拆分成多模块
- parent pom =>
<packaging>pom</packaging>
<modules><module>module-one</module><module>module-two</module></modules>
- 子模块中的 artifactId 和 parent pom 中的 module 保持一致
- 子模块中的 version 和父模块中的 version 保持一致
- 模块之间的依赖关系和普通项目的依赖并无区别
- 可以运行
mvn install
将所有模块安装到本地
模块的继承
- 多个模块间大量的重复代码违反了 DRY 原则
- 将共同元素抽取成为公用的 POM
- 所有的 POM 都隐式继承超级 POM => 超级 POM:<maven安装包>/lib/maven-model-build-<version>/org/apache/maven/model/pom-4.0.0.xml =>
<modelVersion>4.0.0</modelVersion>
-
<dependencyManagement>
=> 为所有子模块定义统一的依赖版本 -
<pluginManagement>
=> 为所有的子模块定义统一的插件版本 -
<profiles></profiles>
=> 可以控制在不同的环境下让 maven 做不同的事情 => 在 maven 命令中使用-P
参数,e.g. => 也可以在本地 ~/.m2/setting.xml 中配置 profiles
知识点
- NoClassDefFoundError vs ClassNotFoundException
- IDEA 和 Maven 在包管理方面遵循同一组依赖模型,但是有时可能会产生不一致的情形。IDEA 就是一个普通的 JVM 进程,它加载了一个 Maven 的 jar 包,之后执行相关的命令
- IDEA 对依赖的理解
- Maven -> Dependencies
- 运行时在控制台打印出来的命令中的 classpath
- Maven 对依赖的理解 => mvn dependency:tree
- IDEA 对依赖的理解
- 调试
- 调试的代码运行在哪个 JVM 里?
- 调试的源代码在哪里?=> 调试的源代码和正在运行的 JVM 中的字节码是一一对应的
- mvnDebug 命令
网友评论