目录
- 1.SpringBoot如何打包成一个可执行jar?
- 2.SpringBoot打包成的jar为何可以直接Java -jar执行?
- 3.一窥SpringBoot初启动
本文主要分享SpringBoot工程项目如何打包成一个可直接通过java -jar执行的jar,并且简单分析其启动步骤原理。
1.SpringBoot如何打包成一个可执行jar?
SpringBoot打包成成一个可执行jar需要依赖一个maven打包插件spring-boot-maven-plugin,如下所示在pom文件结尾的build节点添加依赖,同时将src/main/java和src/main/resources打入jar里面。
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.*</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.*</include> </includes> </resource> </resources> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
此时再执行maven package打包成jar(最后打包完成在target目录下):
引入后其中JarLaunche类代码如下,它包含一个main方法,并且继承一个父类ExecutableArchiveLauncher。
public class JarLauncher extends ExecutableArchiveLauncher { static final String BOOT_INF_CLASSES = "BOOT-INF/classes/"; static final String BOOT_INF_LIB = "BOOT-INF/lib/"; public JarLauncher() { } protected JarLauncher(Archive archive) { super(archive); } protected boolean isNestedArchive(Entry entry) { return entry.isDirectory() ? entry.getName().equals("BOOT-INF/classes/") : entry.getName().startsWith("BOOT-INF/lib/"); } public static void main(String[] args) throws Exception { (new JarLauncher()).launch(args); } }
从main方法进入我们需要关注一个launch方法,其中的三行
- 注册一个jar处理类
- 获取一个类加载器(通过路径加载lib和classs)
- 通过获取主启动类和类加载器运行启动。
protected void launch(String[] args) throws Exception { JarFile.registerUrlProtocolHandler(); ClassLoader classLoader = this.createClassLoader(this.getClassPathArchives()); this.launch(args, this.getMainClass(), classLoader); }
追寻this.getMainClass() 方法,其会指向上述的父类ExecutableArchiveLauncher中,此处就回到了我们开始说的Start-Class配置项。
protected String getMainClass() throws Exception { Manifest manifest = this.archive.getManifest(); String mainClass = null; if (manifest != null) { mainClass = manifest.getMainAttributes().getValue("Start-Class"); } if (mainClass == null) { throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this); } else { return mainClass; } }
getMainClass()方法中的this.archive.getManifest() 就是获取jar下的META-INF/MANIFEST.MF文件。此处追寻到Archive的子类ExplodedArchive中,如下代码获取清单文件:
private File getManifestFile(File root) { File metaInf = new File(root, "META-INF"); return new File(metaInf, "MANIFEST.MF"); }
到此时清单文件中的Start-Class、Spring-Boot-Classes、Spring-Boot-Lib都已找到,准备启动!
回到org.springframework.boot.loader.Launcher的launch方法。
- 当前线程设置类加载器
- 创建主方法并运行(主方法即为Start-Class指向的类)
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception { Thread.currentThread().setContextClassLoader(classLoader); this.createMainMethodRunner(mainClass, args, classLoader).run(); }
以上两行代码具体实现在org.springframework.boot.loader.MainMethodRunner类中,run方法即为最后的启动逻辑
- 通过当前线程类加载器载入清单文件Start-Class配置的主类。
- 加载后获取主类中的main方法(XXXApplication中的main方法)。
- 反射执行该XXXApplication中的main方法。
public MainMethodRunner(String mainClass, String[] args) { this.mainClassName = mainClass; this.args = args != null ? (String[])args.clone() : null; } public void run() throws Exception { Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName); Method mainMethod = mainClass.getDeclaredMethod("main", String[].class); mainMethod.invoke((Object)null, this.args); }
至此,从执行java -jar命令,SpringBoot打包的jar启动到程序中XXXApplication中的main方法这一阶段我们就初步分析完了!
原文地址:https://blog.csdn.net/bk120/article/details/130636882总结:SpringBoot通过引入打包插件spring-boot-maven-plugin将其相关信息写入到java-jar的META-INF/MANIFEST.MF文件中,而后通过清单文件中的主入口Main-Class配置的org.springframework.boot.loader.JarLauncher类加载相关class、lib和应用程序主类Start-Class配置的XXXApplication类,再反射获取XXXApplication类中我们业务定义的main方法启动运行。