Maven

作者: bowen_wu | 来源:发表于2022-08-05 10:48 被阅读0次

    Maven 包管理工具

    1. JVM 世界里面只有全限定类名(Full Qualified Class Name),类的全限定类名唯一确定了一个类
    2. JVM 的工作 => 执行一个类的字节码,假如这个过程中碰到了新的类,加载它
    3. 类路径(Classpath) => JVM 在 classpath 里面寻找类
    4. 包就是把许多类放在一起打的压缩包
    5. 传递性依赖 => 你依赖的类还依赖了别的类
    6. Classpath Hell => 当多个同名类同时出现在 Classpath 中,就是噩梦的开始
    7. 包管理 => 告知 JVM 如何找到所需的第三方类库以及成功地解决其中的冲突问题

    Maven 包管理

    • Maven 在包管理所做的事情 => 查看坐标,根据坐标去中央仓库中定位包,之后查看 pom 文件,找到依赖的包,之后逐个查看坐标,定位包,将所有的包都下载到本地,之后将这些依赖都放置在 classpath 中,供 JVM 去检索,之后启动 JVM
    • 传递性依赖原则 => 决不允许最终 classpath 出现同名不同版本的 jar 包
    • 自动管理传递性依赖
    • 自动解决冲突

    坐标系统

    1. 通过唯一的坐标定位包 => groupId + artifactId + version
    2. 坐标可读性好、简单方便
    3. 语义化版本,方便自动化工具处理
    4. 一旦发布不可修改,实现稳定的构建

    元数据系统

    1. jar 包本身是没有元信息,很难知道依赖谁
    2. 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 去进行相应的工作

    1. javac 编译 main 目录 => compile + provided
    2. javac 编译 test 目录 => compile + provided + test
    3. java 运行所有测试 => compile + test + runtime
    4. java 运行生产代码 => compile + runtime

    冲突解决

    当看到如下异常的时候,意味着包冲突出现了

    • AbstractMethodError
    • NoClassDefFoundError
    • ClassNotFoundException
    • LinkageError

    Maven 解决冲突原则

    1. 最近原则
    2. 最先声明原则

    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> 
      
    • 中央仓库的声明与镜像
    • 中央仓库、快照仓库和插件仓库
      1. 中央仓库 => <repositories></repositories>
      2. 快照仓库 =>
      3. 插件仓库 => <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
    Maven Lifecycle

    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
        1. 在 $PATH 环境变量中找 mvn 可执行程序,找到之后将后面的参数传递给 maven
        2. maven 启动 JVM,通过 System property 传递给 JVM, System property key 为 plugin, value 为 org.flywaydb:flyway-maven-plugin
        3. maven 查找 help 的 plugin,执行 help plugin 的 describe 目标
        4. 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

    artifactory

    Maven 多模块

    • 软件工程的原则 => 高内聚低耦合
    • 模块化的系统 => 用户自由选择所需模块
    • 可能带来性能的提升
    • 多模块 -> 独立的项目 -> 微服务

    拆分成多模块

    1. parent pom =>
      • <packaging>pom</packaging>
      • <modules><module>module-one</module><module>module-two</module></modules>
    2. 子模块中的 artifactId 和 parent pom 中的 module 保持一致
    3. 子模块中的 version 和父模块中的 version 保持一致
    4. 模块之间的依赖关系和普通项目的依赖并无区别
    5. 可以运行 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

    知识点

    1. NoClassDefFoundError vs ClassNotFoundException
    2. IDEA 和 Maven 在包管理方面遵循同一组依赖模型,但是有时可能会产生不一致的情形。IDEA 就是一个普通的 JVM 进程,它加载了一个 Maven 的 jar 包,之后执行相关的命令
      • IDEA 对依赖的理解
        1. Maven -> Dependencies
        2. 运行时在控制台打印出来的命令中的 classpath
      • Maven 对依赖的理解 => mvn dependency:tree
    3. 调试
      • 调试的代码运行在哪个 JVM 里?
      • 调试的源代码在哪里?=> 调试的源代码和正在运行的 JVM 中的字节码是一一对应的
    4. mvnDebug 命令

    相关文章

      网友评论

          本文标题:Maven

          本文链接:https://www.haomeiwen.com/subject/tnzfwrtx.html