帮做ppt网站,中国建设银行个人网站,自学做包装设计的步骤,wordpress手动更新SpringBoot为什么可以打成 jar包启动 简介#xff1a;庆幸的是夜跑的习惯一直都在坚持#xff0c;正如现在坚持写博客一样。最开始刚接触springboot的时候就觉得很神奇#xff0c;当时也去研究了一番#xff0c;今晚夜跑又想起来了这茬事#xff0c;于是想着应该可以记录一…SpringBoot为什么可以打成 jar包启动 简介庆幸的是夜跑的习惯一直都在坚持正如现在坚持写博客一样。最开始刚接触springboot的时候就觉得很神奇当时也去研究了一番今晚夜跑又想起来了这茬事于是想着应该可以记录一下了不至于下次想不来了又去翻资料。
一、SpringBoot生成的jar包是什么 Spring Boot的可执行jar包又称作“fat jar”那什么是fat jar呢在java中将应用程序及其依赖jar一起打包到一个独立的jar中就叫fat jar,它也叫uberJar。springboot的打包方式就是这样将应用程序代码打包到BOOT-INF.classes将依赖包打包到BOOT-INF.lib目录这里我们以xxl-job-admin-2.4.0-SNAPSHOT.jar为例来做说明我们使用反编译工具jd将jar打开目录如下 各目录存放内容如下
BOOT-INF/classes目录存放应用编译后的class文件。 BOOT-INF/lib目录存放应用依赖的第三方JAR包文件。 META-INF目录存放应用打包信息(Maven坐标、pom文件)和MANIFEST.MF文件。 org目录存放SpringBoot相关class文件。
这里我们首先关注一下配置文件MANIFEST.MF内容如下
Manifest-Version: 1.0 Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx Archiver-Version: Plexus Archiver Built-By: user Spring-Boot-Layers-Index: BOOT-INF/layers.idx Start-Class: com.xxl.job.admin.XxlJobAdminApplication Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Spring-Boot-Version: 2.6.7 Created-By: Apache Maven 3.6.1 Build-Jdk: 1.8.0_211 Main-Class: org.springframework.boot.loader.JarLauncher
参考 Oracle 官方对该的说明
Main-ClassJava 规定的 jar 包的启动类这里设置为 spring-boot-loader 项目的 JarLauncher 类进行 Spring Boot 应用的启动
Start-ClassSpring Boot 规定的主启动类这里通过 Spring Boot Maven Plugin 插件打包时会设置为我们定义的 Application 启动类
为什么不直接将我们的 Application 启动类设置为 Main-Class 启动呢 因为通过 Spring Boot Maven Plugin 插件打包后的 jar 包我们的 .class 文件在 BOOT-INF/classes/ 目录下在 Java 默认的 jar 包加载规则下找不到我们的 Application 启动类也就需要通过 JarLauncher 启动加载。当然还有一个原因Java 规定可执行器的 jar 包禁止嵌套其它 jar 包在 BOOT-INF/lib 目录下有我们 Spring Boot 应用依赖的所有第三方 jar 包因此spring-boot-loader 项目自定义实现了 ClassLoader 实现类 LaunchedURLClassLoader支持加载 BOOT-INF/classes 目录下的 .class 文件以及 BOOT-INF/lib 目录下的 jar 包。
二、JarLauncher启动器实现原理
上文描述了Application 的Main-Clas启动类是JarLauncher 类那么接下来我们一起来看看 Spring Boot 的 JarLauncher 这个类
JarLauncher的继承关系如下 JarLauncher全路径是org.springframework.boot.loader.JarLauncher。
public class JarLauncher extends ExecutableArchiveLauncher { private static final String DEFAULT_CLASSPATH_INDEX_LOCATION BOOT-INF/classpath.idx; static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER (entry) - { if (entry.isDirectory()) { return entry.getName().equals(BOOT-INF/classes/); } return entry.getName().startsWith(BOOT-INF/lib/); }; public JarLauncher() { } protected JarLauncher(Archive archive) { super(archive); } Override protected ClassPathIndexFile getClassPathIndex(Archive archive) throws IOException { // Only needed for exploded archives, regular ones already have a defined order if (archive instanceof ExplodedArchive) { String location getClassPathIndexFileLocation(archive); return ClassPathIndexFile.loadIfPossible(archive.getUrl(), location); } return super.getClassPathIndex(archive); } private String getClassPathIndexFileLocation(Archive archive) throws IOException { Manifest manifest archive.getManifest(); Attributes attributes (manifest ! null) ? manifest.getMainAttributes() : null; String location (attributes ! null) ? attributes.getValue(BOOT_CLASSPATH_INDEX_ATTRIBUTE) : null; return (location ! null) ? location : DEFAULT_CLASSPATH_INDEX_LOCATION; } Override protected boolean isPostProcessingClassPathArchives() { return false; } Override protected boolean isSearchCandidate(Archive.Entry entry) { return entry.getName().startsWith(BOOT-INF/); } Override protected boolean isNestedArchive(Archive.Entry entry) { return NESTED_ARCHIVE_ENTRY_FILTER.matches(entry); } public static void main(String[] args) throws Exception { new JarLauncher().launch(args); } }
通过new一个JarLauncher().launch(args)方式进行启动。
public class Jarlauncher{ ... private static final String JAR_MODE_LAUNCHER org.springframework.boot.loader.jarmode.JarModeLauncher; ... protected void launch(String[] args) throws Exception { //判断是否以一个分解模式的方式运行如果是则运行否则只支持规范的jar文件从而选择跳过 if (!isExploded()) { JarFile.registerUrlProtocolHandler(); } //获取类加载器 ClassLoader classLoader createClassLoader(getClassPathArchivesIterator()); //获取系统中的jar模型 String jarMode System.getProperty(jarmode); //加载启动引导类 String launchClass (jarMode ! null !jarMode.isEmpty()) ? JAR_MODE_LAUNCHER : getMainClass(); //启动应用 launch(args, launchClass, classLoader); } }