Maven源码调试工程搭建

思路

我们前面的文章《【曹工杂谈】Maven和Tomcat能有啥联系呢,都穿打补丁的衣服吗》分析了Maven大体的执行阶段,主要包括三个阶段:

  1. 启动类阶段,负责加载框架;

  2. 框架核心(maven core)阶段,主要负责根据参数中的goal,如clean,找到对应的插件的jar包,生成插件对象,解析参数并调用插件代码;

  3. 插件执行阶段,执行完成后,控制权会交还给框架核心,由maven core完成善后工作。

这三个阶段,分别会去不同的地方加载对应的jar包。

大概了解了这个流程后,我们开始思考,我们到底要调试哪一部分的代码。

  • 如果我们要调试阶段一的代码,那么,又分两种情况,是否需要改动阶段一的代码,如果需要改动,那么,就要找到阶段一的jar的源码,然后加载到idea中,并且,启动调试的时候,也需把classpath指定到我们的源码工程;如果不需要改动,那就可以参考:《【曹工杂谈】详解Maven插件调试方法》,直接把jar包引入idea的project classpath即可。
  • 如果我们需要调试阶段二的代码,那么,也分两种情况,需要改动代码,就去找源码,然后引入到idea;同时,阶段一去找阶段二的时候,默认还是去了%MAVEN_HOME%/lib/*.jar下边去找,我们这时候就得纠正它的错误行为,让它转而去我们的源码工程下找阶段二的代码。
  • 如果需要调试阶段三的代码,如果需要改动,默认也是要引入插件的源码工程;但是,阶段二,去找插件代码的时候,要让它去我们的源码工程下找代码是比较困难的(默认就是去本地仓库找,要纠正这个行为,难度较大),因此,就算了。我们还是会引入插件的源码,但是,每次改了插件代码,手动部署到本地仓库就行。

我们接下来,就开始实现上面的三个目标。

搭建阶段三的调试工程

大部分人可能感兴趣的还是插件代码,想自己搭着玩的,可以参考:《【曹工杂谈】详解Maven插件调试方法

但是在这里,我还是需要基于我在gitee上新建的一个仓库,来给大家讲解。后续的源码分析文章(如果有的话)也会基于这个工程来讲解。

代码在这里:https://gitee.com/ckl111/maven-3.8.1-source-learn

下边介绍下目录结构:

  • test-maven-core,用来在这个module的pom上,执行mvn clean等命令
  • plugin-sources,用来存放插件的代码,后续的插件源码都会往这里放
  • plexus-classworlds-source,用来存放阶段一的那个启动类的源码
  • 除此之外,都是maven框架本身的源码,这部分,统称为maven core

下载了源码后,进行idea导入,导入时,选择这个主目录下的pom,这个pom,就是框架核心的pom,默认只会导入框架核心的maven工程;我们还需要手动,把test-maven-core、plugin-sources、plexus-classworlds-source都导入一下(对着pom右键,Mark As Maven Project即可)。

导入完成后,我们默认会有4个root maven工程,其中,最上面的那个Apache Maven,就是maven核心框架。

接下来,也简单了,如果不需要修改代码的话,那就可以在任意位置打断点了。打完断点,怎么触发呢, 下边这样就行了。

我这边已经ok了,你呢?

如果想修改插件代码,改完后,记得mvn install插件到本地仓库。

搭建阶段二的调试工程

如果大家只是想跟着代码debug,那么,直接在maven框架对应的类上打断点就行了,下图就是在maven core这个module上打断点。

如果可能自己想改改maven源码、辅助学习的话,我们就得动点心思。

在此之前,我们需要把整个maven框架核心,先编译一次,因此,在G:\gitee-projects\maven-3.8.1-source-learn(只是我这边的目录)下,执行:

mvn -DskipTests=true install

如果大家有报错,什么rat相关的,可以来这里:

把这个插件元素(元素)删掉。

我这边还遇到了checkstyle报错,同样,也在这个pom里,把checkstyle检查删掉。

最终呢,还是执行ok了。

上面这步ok后,在昨天的文章里《【曹工杂谈】Maven和Tomcat能有啥联系呢,都穿打补丁的衣服吗》,我们说了,maven的启动类,是去下边这个地方,找maven core代码的:

F:\tools\apache-maven-3.8.1-bin\apache-maven-3.8.1\bin\m2.conf.

文件内容原本是这样的:

main is org.apache.maven.cli.MavenCli from plexus.core

set maven.conf default ${maven.home}/conf

[plexus.core]
load ${maven.conf}/logging
optionally ${maven.home}/lib/ext/*.jar
load ${maven.home}/lib/*.jar

其中,load ${maven.home}/lib/*.jar就加载了maven的核心框架jar。这个地方,我们得改改,让它还得去我们idea中源码工程下加载class。

main is org.apache.maven.cli.MavenCli from plexus.core

set maven.conf default ${maven.home}/conf

[plexus.core]
load ${maven.conf}/logging
optionally ${maven.home}/lib/ext/*.jar
load G:\gitee-projects\maven-3.8.1-source-learn\maven-artifact\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-builder-support\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-compat\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-core\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-embedder\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-model\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-model-builder\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-plugin-api\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-repository-metadata\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-resolver-provider\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-settings\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-settings-builder\target\classes
load G:\gitee-projects\maven-3.8.1-source-learn\maven-slf4j-provider\target\classes
load ${maven.home}/lib/*.jar

也就是去我们得target下加载classes。

这样呢,我们就可以改动我们idea里导入的maven 核心框架的代码,然后compile了,compile后,class进入到对应的target目录下,正好就在上文的m2.conf里的路径中。

为了验证是否生效,我们随便在maven核心框架打了个断点,看看当前类的类加载器的路径,发现确实已经ok了。

搭建阶段一的调试工程

阶段一,也就是那个启动jar包,对应的源码在:

我们只需要在启动测试工程里,把classpath指向这里的target目录,而不是maven安装目录下的plexus-classworlds-2.6.0.jar就可以了。

我们把这段复制出来,然后把其中,指向jar包的替换掉:

-classpath "G:\gitee-projects\maven-3.8.1-source-learn\target;F:\tools\apache-maven-3.8.1-bin\apache-maven-3.8.1\boot\plexus-classworlds.license;C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.1\lib\idea_rt.jar"

把这段贴到maven执行的参数中即可。

总结

汇总一下:

  1. 阶段一,指定classpath来调试我们的源码工程:

    -classpath "G:\gitee-projects\maven-3.8.1-source-learn\target;F:\tools\apache-maven-3.8.1-bin\apache-maven-3.8.1\boot\plexus-classworlds.license;C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.1\lib\idea_rt.jar"
  2. 阶段二,框架核心阶段,通过修改maven安装目录下的m2.conf的内容来实现

  3. 阶段三,插件源码阶段,修改源码后,通过编译部署到本地仓库后实现

【曹工杂谈】Maven源码调试工程搭建的更多相关文章

  1. 曹工说Redis源码(2)-- redis server 启动过程解析及简单c语言基础知识补充

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  2. 曹工说Redis源码(3)-- redis server 启动过程完整解析(中)

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  3. 曹工说Redis源码(4)-- 通过redis server源码来理解 listen 函数中的 backlog 参数

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  4. 曹工说Redis源码(5)-- redis server 启动过程解析,以及EventLoop每次处理事件前的前置工作解析(下)

    曹工说Redis源码(5)-- redis server 启动过程解析,eventLoop处理事件前的准备工作(下) 文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis ...

  5. 曹工说Redis源码(6)-- redis server 主循环大体流程解析

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  6. 曹工说Redis源码(7)-- redis server 的周期执行任务,到底要做些啥

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  7. 曹工说Redis源码(8)--面试时,redis 内存淘汰总被问,但是总答不好

    文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...

  8. 曹工说JDK源码(4)--抄了一小段ConcurrentHashMap的代码,我解决了部分场景下的Redis缓存雪崩问题

    曹工说JDK源码(1)--ConcurrentHashMap,扩容前大家同在一个哈希桶,为啥扩容后,你去新数组的高位,我只能去低位? 曹工说JDK源码(2)--ConcurrentHashMap的多线 ...

  9. 曹工说Redis源码(1)-- redis debug环境搭建,使用clion,达到和调试java一样的效果

    概要 最近写了spring系列,这个系列还在进行中,然后有些同学开始叫我大神,然后以为我各方面都比较厉害,当然了,我是有自知之明的,大佬大神什么的,当作一个称呼就好,如果真的以为自己就是大神,那可能就 ...

随机推荐

  1. 第一章 Vant的引入和基本使用

    Vant UI组件库基本使用 一.Vant官网 https://youzan.github.io/vant/#/zh-CN/ 二.安装Vant npm install --save vant 三.安装 ...

  2. vue中this.$router.push()路由传值和获取的两种常见方法

    1.路由传值   this.$router.push() (1) 路由跳转使用router.push()方法,这个方法会向history栈添加一个新纪录,所以,当用户点击浏览器后退按钮时,会回到之前的 ...

  3. 8.7考试总结(NOIP模拟)[Smooth·Six·Walker]

    前言 踩了挺多以前没踩过的坑... T1 一开始是打了一个 60pts 的 DFS ,在与暴力拍了几组数据保证正确性之后, 突然想到 BFS 可能会更快一些,然后就又码了一个 BFS,又和 DFS 拍 ...

  4. 利用共享内存实现比NCCL更快的集合通信

    作者:曹彬 | 旷视 MegEngine 架构师 简介 从 2080Ti 这一代显卡开始,所有的民用游戏卡都取消了 P2P copy,导致训练速度显著的变慢.针对这种情况下的单机多卡训练,MegEng ...

  5. CRC校验原理和verilog实现方法(三)

    1 代码生成 verilog实现CRC校验,可以充分发挥FPGA的硬件特性,即并行运算的能力. 具体实现方式,可以参考我上一篇博客,关键是用线性反馈移位寄存器表示出多项式,另外注意校验数据高位在先.然 ...

  6. Java方法01——什么是方法

    例子 package method;public class Demon02 { //main 方法 public static void main(String[] args) { //实际参数:实 ...

  7. 守护线程_daemon

    守护线程_daemon 线程分为用户线程和守护线程 虚拟机必须确保用户线程(main)执行完毕 虚拟机不用等待守护线程(gc)执行完毕 如:后台记录操作日志,监控内存,垃圾回收等等 测试案例: pac ...

  8. [C++]-set 集合

    set集合用于存放一组无重复的元素.由于集合本身是有序的,所以可以高效地查找指定元素,也可以方便地得到指定大小范围地元素在容器中所处区间. 代码 #include<iostream> #i ...

  9. RationalRose使用

    UML 概述 什么是 UML? Unified Modeling Language (UML)又称统一建模语言或标准建 模语言,是始于 1997 年一个 OMG 标准,它是一个支持模型化 和软件系统开 ...

  10. Special Forms and Syntax Sugars in Clojure

    (...): function literals, p40, 64; '(...): suppress evaluation, p24; _(...): comments, p18; ".. ...