Maven源码阅读
Main函数在哪里?
在apache-maven-3.8.3\apache-maven\src\bin\mvn的109行
108 CLASSWORLDS_JAR=`echo "${MAVEN_HOME}"/boot/plexus-classworlds-*.jar`
109 CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
org.codehaus.plexus.classworlds.launcher.Launcher(这个是我们梦开始的地方:main函数就在这里)
org.codehaus.plexus.classworlds.launcher.Launcher#main
public static void main( String[] args )
{
try
{
int exitCode = mainWithExitCode( args ); // TODO 核心方法
System.exit( exitCode );
}
catch ( Exception e )
{
e.printStackTrace();
System.exit( 100 );
}
}
如何启动?
说明: idea开发工具
JVM参数
说明: 具体参数可以参考idea启动参数
-Dclassworlds.conf=E:\#learning\apache-maven-3.8.3\apache-maven\src\bin\m2.conf -Dmaven.home=D:\apache-maven-3.8.3 -Dmaven.conf=D:\apache-maven-3.8.3\conf -Dclassworlds.conf=D:\apache-maven-3.5.4\bin\m2.conf -Dmaven.multiModuleProjectDirectory=E:\#adt\audit-trail-ms -Duser.dir=E:\#adt\audit-trail-ms
自己的疑惑:
maven.multiModuleProjectDirectory和user.dir参数有什么区别, 默认user.dir是我们源码工程路径maven.multiModuleProjectDirectory没有默认值,必须填写一个,我们mvn命令执行的路径是user.dir配置的, 为什么必须配置两个,这个地方还需要深入理解,有知道的伙伴评论区给出你的理解,谢谢!
程序参数示例
clean package
脚本启动的原理
maven本质是执行命令,因为maven构建框架是固定的,变化的是插件,因此一级命令(我们通常看到的mvn -v查看maven版本,就是这个mvn)是一致的:
一级命令取决于脚本文件的名称,毫无疑问,脚本文件的逻辑含义是maven核心之一,接下来深入看看这些脚本干了啥,以mvn.cmd为例(与mvn相比文件除了语法差异,其他并无差别)进行解析。
一次你完全可以修改mvn名称,改变命令: 示例 mvn -> mvn -副本
D:\apache-maven-3.8.3\bin>mvn - 副本 -v
Apache Maven 3.8.3 (ff8e977a158738155dc465c6a97ffaf31982d739)
Maven home: D:\apache-maven-3.8.3
Java version: 1.8.0_151, vendor: Oracle Corporation, runtime: D:\Java\jdk1.8.0_151\jre
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
命令行启动cmd命令
for %%i in ("%MAVEN_HOME%"\boot\plexus-classworlds-*) do set CLASSWORLDS_JAR="%%i"
@REM 指定执行命令的jar
set CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
"%JAVACMD%" ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %CLASSWORLDS_JAR% ^
@REM classworlds 配置文件地址
"-Dclassworlds.conf=%MAVEN_HOME%\bin\m2.conf" ^
@rem maven home
"-Dmaven.home=%MAVEN_HOME%" ^
@rem jansi 的位置
"-Dlibrary.jansi.path=%MAVEN_HOME%\lib\jansi-native" ^
@rem 命令执行的位置, 默认是maven工程目录
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
@rem 自动调用classworlds.launcher.Launcher的main方法, 并吧命令行参数传给main的args
%CLASSWORLDS_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
脚本主要干了四件事
- 关联配置m2.conf;
- 关联入口plexus-classworlds-2.6.0.jar(去版本关联)及入口方法;
- 写入配置参数;
- 截取命令输入作为参数执行2中方法;
这里我们通过源码直接启动java代码方式调试代码
org.codehaus.plexus.classworlds.launcher.Launcher#main
启动的第一行代码, 梦开始的地方
mainWithExitCode
launcher.configure( is ); 加载配置
launcher.launch( args ); 梦想起航
public void launch( String[] args )
throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
NoSuchRealmException
{
try
{
launchEnhanced( args );
return;
}
catch ( NoSuchMethodException e )
{
}
launchStandard( args );
}
launchEnhanced: 增强启动
Object ret = mainMethod.invoke( mainClass, new Object[]{args, getWorld()} ); 反射调用
MavenCli#main(String[], ClassWorld): Maven 命令行
doMain: 核心流程
public int doMain(CliRequest cliRequest) {
PlexusContainer localContainer = null;
try {
initialize(cliRequest);
cli(cliRequest);
properties(cliRequest);
logging(cliRequest);
informativeCommands(cliRequest);
version(cliRequest);
localContainer = container(cliRequest);
commands(cliRequest);
configure(cliRequest);
toolchains(cliRequest);
populateRequest(cliRequest);
encryption(cliRequest);
repository(cliRequest);
return execute(cliRequest);
} catch (ExitException e) {
return e.exitCode;
} catch (UnrecognizedOptionException e) {
return 1;
} catch (BuildAbort e) {
CLIReportingUtils.showError(slf4jLogger, "ABORTED", e, cliRequest.showErrors);
return 2;
} catch (Exception e) {
CLIReportingUtils.showError(slf4jLogger, "Error executing Maven.", e, cliRequest.showErrors);
return 1;
} finally {
if (localContainer != null) {
localContainer.dispose();
}
}
}
MavenCli#execute: maven执行的核心入口
private int execute(CliRequest cliRequest) throws MavenExecutionRequestPopulationException {
MavenExecutionRequest request = executionRequestPopulator.populateDefaults(cliRequest.request);
eventSpyDispatcher.onEvent(request);
MavenExecutionResult result = maven.execute(request);
eventSpyDispatcher.onEvent(result);
eventSpyDispatcher.close();
}
- org.apache.maven.DefaultMaven#execute
? org.apache.maven.DefaultMaven#doExecute
? MavenExecutionResult result = new DefaultMavenExecutionResult();
? org.apache.maven.DefaultMaven#doExecute
private MavenExecutionResult doExecute(MavenExecutionRequest request, MavenSession session,
MavenExecutionResult result, DefaultRepositorySystemSession repoSession) {
eventCatapult.fire(ExecutionEvent.Type.ProjectDiscoveryStarted, session, null);
Result<? extends ProjectDependencyGraph> graphResult = buildGraph(session, result);
try {
session.setProjectMap(getProjectMap(session.getProjects()));
} catch (DuplicateProjectException e) {
return addExceptionToResult(result, e);
}
WorkspaceReader reactorWorkspace;
try {
reactorWorkspace = container.lookup(WorkspaceReader.class, ReactorReader.HINT);
} catch (ComponentLookupException e) {
return addExceptionToResult(result, e);
}
repoSession.setWorkspaceReader(
ChainedWorkspaceReader.newInstance(reactorWorkspace, repoSession.getWorkspaceReader()));
repoSession.setReadOnly();
graphResult = buildGraph(session, result);
try {
result.setProject(session.getTopLevelProject());
validatePrerequisitesForNonMavenPluginProjects(session.getProjects());
lifecycleStarter.execute(session);
validateActivatedProfiles(session.getProjects(), request.getActiveProfiles());
} finally {
}
return result;
}
-
org.apache.maven.lifecycle.internal.LifecycleStarter#execute builder.build( session, reactorContext, projectBuilds, taskSegments, reactorBuildStatus );
-
org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder#build 还有一个多线程的,类似 public void build(MavenSession session, ReactorContext reactorContext, ProjectBuildList projectBuilds,
List<TaskSegment> taskSegments, ReactorBuildStatus reactorBuildStatus) {
for (TaskSegment taskSegment : taskSegments) {
for (ProjectSegment projectBuild : projectBuilds.getByTaskSegment(taskSegment)) {
try {
lifecycleModuleBuilder.buildProject(session, reactorContext, projectBuild.getProject(),
taskSegment);
if (reactorBuildStatus.isHalted()) {
break;
}
} catch (Exception e) {
break;
}
}
}
}
-
org.apache.maven.lifecycle.internal.LifecycleModuleBuilder#buildProject 核心中的核心 注释参考: https://www.codenong.com/cs106269680/ 如有侵权联系删除
- 设置构建的初始化属性
- 校验本地依赖库的可访问性
- 创建RepositorySystemSession
- 创建MavenSession
- 执行AbstractLifecycleParticipant.afterSessionStart(session)
- 获取校验pom对象error的对象
- 创建ProjectDependencyGraph用以调整–projects和reactor模式(确保所有传递到ReactorReader的项目仅仅是指定的项目)
- 创建ReactorReader用以获取对象映射(getProjectMap( projects )),在获取的时候会对对象做唯一性校验,这些对象是从第6步中获取的对象集合
- 执行AbstractLifecycleParticipant.afterProjectsRead(session)后置处理
- 创建ProjectDependencyGraph,不用再调整,第7步已经做了这个工作,这里要完成对AbstractLifecycleParticipants的拓扑排序,可能会改变依赖进而影响构建顺序
- 开始执行LifecycleStarter.start()生命周期开始
//============================================================================= //上述为构建执行的准备步骤,接下来是构建的详细步骤,会涉及到获取maven插件(plugin)配置的获取以及插件goal的执行,直到每个插件执行自己的构建逻辑:
- 每个插件的goal执行是基于某个生命周期运行的,这里是执行插件某个goal的入口:org.apache.maven.lifecycle.internal.LifecycleStarter#execute(MavenSession)
- 执行DefaultLifecycleTaskSegmentCalculator#calculateTaskSegments,获取配置插件对应的goal集合,如果找不到就会默认用MavenSession.getTopLevelProject().getDefaultGoal(),走默认maven构建(获得的构建任务放在TaskSegment里)
- 根据2中得出的构建任务,计算得出构建对象集合,封装在ProjectBuildList里,实际上是做了一个映射,当前MavenSession的所有projects都必须执行TaskSegment集合里的任务
- 获取执行参数里的builderId,分为两种,单例构建或多线程构建,默认是多线程,可以通过-T命令设置线程数量,然后通过指定构建器着手进行构建工作
- 构建逻辑,单例构建遍历任务集合(TaskSegment)与构建对象集合ProjectBuildList逐一进行构建,多线程按照指定的线程数量作为上限进行并发构建
- 一个项目(project)构建策略,计算得出当前项目的构建计划MavenExecutionPlan,统一Project中的PluginManagement与BuildPlugin(构建模块的版本),获得MojoExecution,
public void buildProject(MavenSession session, MavenSession rootSession, ReactorContext reactorContext,
MavenProject currentProject, TaskSegment taskSegment) {
session.setCurrentProject(currentProject);
sessionScope.enter(reactorContext.getSessionScopeMemento());
sessionScope.seed(MavenSession.class, session);
try {
eventCatapult.fire(ExecutionEvent.Type.ProjectStarted, session, null);
MavenExecutionPlan executionPlan = builderCommon.resolveBuildPlan(session, currentProject, taskSegment,
new HashSet<Artifact>());
List<MojoExecution> mojoExecutions = executionPlan.getMojoExecutions();
projectExecutionListener.beforeProjectLifecycleExecution(
new ProjectExecutionEvent(session, currentProject, mojoExecutions));
mojoExecutor.execute(session, mojoExecutions, reactorContext.getProjectIndex());
} catch (Throwable t) {
} finally {
}
}
|