很多初学者会很疑惑SpringBoot是如何将应用代码和所有依赖打包成一个单独的Jar包的,因为传统的Java项目打包成Jar包后,需要通过-classpath属性指定依赖,才能运行。今天就来分析讲解一下SpringBoot的启动原理。SpringBoot打包插件SpringBoot提供了一个maven项目打包插件spring-boot-maven-plugin,如下:/artifactId>可以方便的将SpringBoot项目打包成jar包。这样一来,我们就不需要再部署Tomcat、Jetty等Web服务器容器了。我们先来看看SpringBoot的打包结构是什么样子的。打开target目录,发现有两个jar包:其中springboot-0.0.1-SNAPSHOT.jar是通过SpringBoot提供的打包插件以新的格式打包的。它变成了一个FatJar,里面包含了所有的依赖;而springboot-0.0.1-SNAPSHOT.jar.original是Java原生打包方式生成的,只包含项目本身的内容。SpringBootFatJar的组织结构我们将SpringBoot的可执行Jar展开后的结构如下:BOOT-INF目录:包含我们的项目代码(classes目录)和需要的依赖(lib目录);META-INF目录:通过MANIFEST.MF文件提供Jar包的元数据,声明jar的启动类;org.springframework.boot.loader:SpringBoot的loader代码,实现了JarinJar加载的神奇源码。我们看到,如果去掉BOOT-INF目录,这将是一个很普通的标准Jar包,包括meta信息和可执行代码部分,其/META-INF/MAINFEST.MF指定了Jar包的启动meta信息,org.springframework.boot.loader执行相应的逻辑操作。MAINFEST.MF的元信息内容如下:Manifest-Version:1.0Spring-Boot-Classpath-Index:BOOT-INF/classpath.idxImplementation-Title:springbootImplementation-Version:0.0.1-SNAPSHOTSpring-Boot-Layers-Index:BOOT-INF/layers.idxStart-Class:com.listenvision.SpringbootApplicationSpring-Boot-Classes:BOOT-INF/classes/Spring-Boot-Lib:BOOT-INF/lib/Build-Jdk-Spec:1.8Spring-Boot-Version:2.5.6Created-By:MavenJarPlugin3.2.0Main-Class:org.springframework.boot.loader.JarLauncher相当于一个Properties配置文件,每一行是一个配置项。我们重点关注两个配置项:Main-Class配置项:Java指定的jar包的启动类,设置为spring-boot-loader项目的JarLauncher类,用于启动SpringBoot应用。Start-Class配置项:SpringBoot指定的主要启动类,这里设置为我们定义的Application类。Spring-Boot-Classes配置项:指定加载应用类的入口点。Spring-Boot-Lib配置项:指定加载应用所依赖的库。启动原理SpringBoot的启动原理如下图所示:源码分析JarLauncherJarLauncher类是SpringBootjar包的启动类。完整的类图如下:WarLauncher类是SpringBootwar包的启动类。启动类org.springframework.boot.loader.JarLauncher并不是引入到项目中,而是通过spring-boot-maven-plugin插件重新打包添加的。接下来我们先来看下JarLauncher的源代码,比较简单,如下图所示:publicclassJarLauncherextendsExecutableArchiveLauncher{privatestaticfinalStringDEFAULT_CLASSPATH_INDEX_LOCATION="BOOT-INF/classpath.idx";staticfinalEntryFilterNESTED_ARCHIVE_ENTRY_FILTER=(entry)->{if(entry.isDirectory()){returnentry.getName().equals("BOOT-INF/classes/");}}returnentry.getName().startsWith("BOOT-INF/lib/");};publicJarLauncher(){}protectedJarLauncher(Archivearchive){super(archive);}@OverrideprotectedClassPathIndexFilegetClassPathIndex(Archivearchive)throwsIOException{//只有展开的存档需要,常规的已经有定义的顺序if(archiveinstanceofExplodedArchive){Stringlocation=getClassPathIndexFileLocation(archive);返回ClassPathIndexFile.loadIfPossible(archive.getUrl(),地点);}返回super.getClassPathIndex(存档);}privateStringgetClassPathIndexFileLocation(Archivearchive)抛出IOException{Manifestmanifest=archive.getManifest();属性attributes=(manifest!=null)?清单.getMainAttributes():空;字符串位置=(属性!=空)?attributes.getValue(BOOT_CLASSPATH_INDEX_ATTRIBUTE):空;返回(位置!=null)?位置:DEFAULT_CLASSPATH_INDEX_LOCATION;}@OverrideprotectedbooleanisPostProcessingClassPathArchives(){returnfalse;}@OverrideprotectedbooleanisSearchCandidate(Archive.Entryentry){returnentry.getName().startsWith("BOOT-INF/");}@OverrideprotectedbooleanisNestedArchive(Archive.Entryentry){returnNESTED_ARCHIVE_ENTRY_FILTER.matches(entry);}publicstaticvoidmain(String[]args)抛出异常{//调用基类Launcher定义的launch方法newJarLauncher().launch(args);}}主要依赖于它的main方法,调用了基类Launcher定义的launch方法,而Launcher是ExecutableArchiveLauncher的父类下面我们来看看Launcher类源代码:LauncherpublicabstractclassLauncher{privatestaticfinalStringJAR_MODE_LAUNCHER="org.springframework.boot.loader.jarmode.JarModeLauncher";protectedvoidlaunch(String[]args)throwsException{if(!isExploded()){JarFile.registerUrlProtocolHandler();}ClassLoaderclassLoader=createClassLoader(getClassPathArchivesIterator());StringjarMode=System.getProperty("jarmode");字符串launchClass=(jarMode!=null&&!jarMode.isEmpty())?JAR_MODE_LAUNCHER:getMainClass();启动(参数,启动类,类加载器);}@DeprecatedprotectedClassLoadercreateClassLoader(List
