Spring Boot的可执行jar包又称为“fat jar”,包含所有第三方依赖,同时内嵌了web容器。jar包的生成主要依赖spring-boot-maven-plugin中,通过RepackageMojo类实现,主要方法为execute;
经过package后,会生成两个jar包,其中后缀为“.original”的jar包为普通包含业务代码的jar包,而另一个jar包则为可启动服务的jar包。
RepackageMojo
RepackageMojo的execute方法如下:
public void execute() throws MojoExecutionException, MojoFailureException {
if (this.project.getPackaging().equals("pom")) {
this.getLog().debug("repackage goal could not be applied to pom project.");
} else if (this.skip) {
this.getLog().debug("skipping repackaging as per configuration.");
} else {
this.repackage();
}
}
在该方法中分别判断了是否为pom项目和是否跳过,之后则执行repackage方法,repackage方法的源码如下所示:
private void repackage() throws MojoExecutionException {
//获得maven生成的普通jar包
Artifact source = this.getSourceArtifact();
//target为最终要生成的jar文件
File target = this.getTargetFile();
//创建打包器
Repackager repackager = this.getRepackager(source.getFile());
//获得运行依赖
Set<Artifact> artifacts = this.filterDependencies(this.project.getArtifacts(), this.getFilters(this.getAdditionalFilters()));
//转换依赖格式
ArtifactsLibraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack, this.getLog());
try {
//获得启动脚本
LaunchScript launchScript = this.getLaunchScript();
//执行重新打包
repackager.repackage(target, libraries, launchScript);
} catch (IOException var7) {
throw new MojoExecutionException(var7.getMessage(), var7);
}
//将原来的jar包增加“.original”后缀
this.updateArtifact(source, target, repackager.getBackupFile());
}
其中重要步骤为创建打包器的getRepackager方法和执行打包的repackage方法:
getRepackager
getRepackager主要负责创建打包器,并根据layout确定生成的可执行文件类型:
private Repackager getRepackager(File source) {
//新建打包器
Repackager repackager = new Repackager(source, this.layoutFactory);
repackager.addMainClassTimeoutWarningListener(new RepackageMojo.LoggingMainClassTimeoutWarningListener());
//mainClass指定入口类
repackager.setMainClass(this.mainClass);
if (this.layout != null) {
this.getLog().info("Layout: " + this.layout);
//确定打包类型
repackager.setLayout(this.layout.layout());
}
return repackager;
}
打包类型主要包括:
public static enum LayoutType {
JAR(new Jar()),
WAR(new War()),
ZIP(new Expanded()),
DIR(new Expanded()),
NONE(new None());
private final Layout layout;
private LayoutType(Layout layout) {
this.layout = layout;
}
public Layout layout() {
return this.layout;
}
}
repackage
repackage方法主要实现打包的具体过程:
public void repackage(File destination, Libraries libraries, LaunchScript launchScript) throws IOException {
//校验目标文件是否合法
if (destination != null && !destination.isDirectory()) {
//校验依赖库是否存在
if (libraries == null) {
throw new IllegalArgumentException("Libraries must not be null");
} else {
//校验文件类型layout是否存在
if (this.layout == null) {
this.layout = this.getLayoutFactory().getLayout(this.source);
}
destination = destination.getAbsoluteFile();
File workingSource = this.source;
//校验是否已经打包
if (!this.alreadyRepackaged() || !this.source.equals(destination)) {
//是否已经存在,需要重新打包
if (this.source.equals(destination)) {
workingSource = this.getBackupFile();
workingSource.delete();
this.renameFile(this.source, workingSource);
}
destination.delete();
try {
//jar写入工具
JarFile jarFileSource = new JarFile(workingSource);
Throwable var6 = null;
try {
//打包
this.repackage(jarFileSource, destination, libraries, launchScript);
} catch (Throwable var22) {
var6 = var22;
throw var22;
} finally {
if (jarFileSource != null) {
if (var6 != null) {
try {
jarFileSource.close();
} catch (Throwable var21) {
var6.addSuppressed(var21);
}
} else {
jarFileSource.close();
}
}
}
} finally {
if (!this.backupSource && !this.source.equals(workingSource)) {
this.deleteFile(workingSource);
}
}
}
}
} else {
throw new IllegalArgumentException("Invalid destination");
}
}
参考
(《Spring Boot 技术内幕》)