摘要:也就是說(shuō),可以執(zhí)行成功,是因?yàn)橹懒说穆窂剑f(shuō)明在啟動(dòng)調(diào)用之前,指定了的位置。判斷當(dāng)前的介質(zhì),是啟動(dòng),還是啟動(dòng)。
Spring-Boot啟動(dòng)之前做了哪些事? Spring Boot Jar文件探究
初始化一個(gè)Spring 應(yīng)用,添加如下依賴(lài)
4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.6.RELEASE com.fxipp.spring first-app-by-gui 0.0.1-SNAPSHOT first-app-by-gui Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin
執(zhí)行mvn package命令打包,查看jar包的目錄結(jié)構(gòu)
. ├── BOOT-INF │?? ├── classes │?? │?? ├── application.properties │?? │?? └── com │?? │?? └── fxipp │?? │?? └── spring │?? │?? └── FirstAppByGuiApplication.class │?? └── lib │?? ├── classmate-1.4.0.jar │?? ├── hibernate-validator-6.0.17.Final.jar │?? ├── jackson-annotations-2.9.0.jar │?? ├── jackson-core-2.9.9.jar │?? ...... ├── META-INF │?? ├── MANIFEST.MF │?? └── maven │?? └── com.fxipp.spring │?? └── first-app-by-gui │?? ├── pom.properties │?? └── pom.xml └── org └── springframework └── boot └── loader ├── ExecutableArchiveLauncher.class ├── JarLauncher.class ├── LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class ├── LaunchedURLClassLoader.class ├── ...... ├── archive │?? ├── Archive$Entry.class │?? ├── Archive$EntryFilter.class │?? ├── Archive.class │?? ├── ...... ├── data │?? ├── RandomAccessData.class │?? ├── RandomAccessDataFile$1.class │?? ├──...... ├── jar │?? ├── AsciiBytes.class │?? ├── Bytes.class │?? ├── ...... └── util └── SystemPropertyUtils.class 18 directories, 91 files
文件結(jié)構(gòu)比較復(fù)雜,解釋一下
BOOT-INF/classes: 存放應(yīng)用編譯后的class文件;
BOOT-INF/lib:class path目錄, 存放應(yīng)用依賴(lài)的jar包;
META-INF: 存放應(yīng)用的元信息,如MANIFEST.MF文件;
org:存放Spring Boot自身的class文件;
Jar文件的執(zhí)行器: Spring Boot Loader我們先從MANIFEST.MF文件查看
Manifest-Version: 1.0 Implementation-Title: first-app-by-gui Implementation-Version: 0.0.1-SNAPSHOT Start-Class: com.fxipp.spring.FirstAppByGuiApplication Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Build-Jdk-Spec: 1.8 Spring-Boot-Version: 2.1.6.RELEASE Created-By: Maven Archiver 3.4.0 Main-Class: org.springframework.boot.loader.JarLauncher
里面記錄了應(yīng)用的元信息,Spring的版本,應(yīng)用的版本,Maven的版本,Main-Class等信息。不難發(fā)現(xiàn),MainClass指向的是org.springframework.boot.loader.JarLauncher(以下簡(jiǎn)稱(chēng)JarLauncher),而不是我們自己編寫(xiě)的com.fxipp.spring.FirstAppByGuiApplication。
JarLauncher從名字看出是一個(gè)jar的執(zhí)行器,他的class文件位于org.springframework.boot.loader目錄下,可見(jiàn)它是Spring自身的class文件。
JarLauncher的GAV org.springframework.boot:spring-boot-loader:2.1.6.RELEASE
通常情況下,他會(huì)在spring-boot-starter-parent引入到應(yīng)用中,既然main-class指向到是JarLauncher,那我們也可以直接執(zhí)行java org.springframework.boot.loader.JarLauncher,也可以啟動(dòng)Spring項(xiàng)目的。
java org.springframework.boot.loader.JarLauncher . ____ _ __ _ _ / / ___"_ __ _ _(_)_ __ __ _ ( ( )\___ | "_ | "_| | "_ / _` | / ___)| |_)| | | | | || (_| | ) ) ) ) " |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.6.RELEASE) 2019-06-19 20:30:52.202 INFO 3094 --- [ main] c.fxipp.spring.FirstAppByGuiApplication : Starting FirstAppByGuiApplication on fangxideMacBook-Pro.local with PID 3094 (/Users/fangxi/Java/workspace/default/spring-boot/first-app-by-gui/target/temp/BOOT-INF/classes started by fangxi in /Users/fangxi/Java/workspace/default/spring-boot/first-app-by-gui/target/temp)
既然可以執(zhí)行,那就說(shuō)明了,JarLauncher這個(gè)類(lèi)才是Spring項(xiàng)目真正的入口。如果我們執(zhí)行自己寫(xiě)的com.fxipp.spring.FirstAppByGuiApplication會(huì)怎么樣?
? classes java com.fxipp.spring.FirstAppByGuiApplication Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication at com.fxipp.spring.FirstAppByGuiApplication.main(FirstAppByGuiApplication.java:10) Caused by: java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication at java.net.URLClassLoader.findClass(URLClassLoader.java:382) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ... 1 more
啟動(dòng)報(bào)錯(cuò),原因是找不到org.springframework.boot.SpringApplication這個(gè)類(lèi),說(shuō)白了就是沒(méi)有指定Class Path,Spring Boot應(yīng)用的Class Path目錄是BOOT-INF/lib。
也就是說(shuō),JarLauncher可以執(zhí)行成功,是因?yàn)镾pring Boot知道了Class Path的路徑,說(shuō)明JarLauncher在啟動(dòng)調(diào)用com.fxipp.spring.FirstAppByGuiApplication之前,指定了Class Path的位置。
JarLauncher的代碼如下
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); } @Override protected boolean isNestedArchive(Archive.Entry entry) { if (entry.isDirectory()) { return entry.getName().equals(BOOT_INF_CLASSES); } return entry.getName().startsWith(BOOT_INF_LIB); } public static void main(String[] args) throws Exception { new JarLauncher().launch(args); } }
Archive.Entry:這個(gè)類(lèi)對(duì)對(duì)象,代編jar包中的資源文件。
isNestedArchive方法判斷entry對(duì)象是不是位于jar包內(nèi),如果在jar內(nèi)部,返回true。如果不在jar包里面,也就是我們解壓了jar包,返回false。
重點(diǎn)看launch(String[])方法
protected void launch(String[] args) throws Exception { // 1 JarFile.registerUrlProtocolHandler(); // 2 ClassLoader classLoader = createClassLoader(getClassPathArchives()); // 3 launch(args, getMainClass(), classLoader); }
這個(gè)方法一共3步
擴(kuò)展JAR協(xié)議
JDK默認(rèn)支持file、http、jar等協(xié)議,所以JDK內(nèi)部有默認(rèn)的實(shí)現(xiàn),位于sun.net.www.protocol包下。
JarFile.registerUrlProtocolHandler();這個(gè)方法將org.springframework.boot.loader包下對(duì)應(yīng)的JAR協(xié)議實(shí)現(xiàn),覆蓋原有的JAR實(shí)現(xiàn)。
因?yàn)樵械腏AR實(shí)現(xiàn),ClassPath是我們自己配置環(huán)境變量的時(shí)候制定的,不是BOOT-INF/lib。
創(chuàng)建一個(gè)classloader,用于加載JarLauncher類(lèi),因?yàn)閖ar包可能會(huì)被解壓,解壓前和解壓后的的ClassLoader是不同的。
調(diào)用launch方法,將參數(shù)傳遞。
args是我們自己指定的參數(shù)。
getMainClass()是獲取MANIFEST.MF文件里面Statr-Class屬性,也就是獲取我們自定義主類(lèi)的Class 文件地址。
傳遞推出的類(lèi)加載器
launch方法
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception { Thread.currentThread().setContextClassLoader(classLoader); createMainMethodRunner(mainClass, args, classLoader).run(); } protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) { return new MainMethodRunner(mainClass, args); }
public class MainMethodRunner { private final String mainClassName; private final String[] args; public MainMethodRunner(String mainClass, String[] args) { this.mainClassName = mainClass; this.args = (args != null) ? 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(null, new Object[] { this.args }); } }
launch方法分析:
將ClassLoader放入當(dāng)前線程里面的ClassLoader里面
創(chuàng)建MainMethodRunner對(duì)象,調(diào)用里面的run()方法。
run()方法先獲取到之前設(shè)定的ClassLoader。
利用ClassLoader加載Start-Class之類(lèi)的類(lèi),也就是我們自己的主類(lèi)。
獲取主類(lèi)里面的main方法,通過(guò)反射執(zhí)行。
總結(jié)通過(guò)分析,我們可以看出,Spring Boot Loader在調(diào)用我們自己的主類(lèi)之前,主要做了三件事
擴(kuò)展JDK默認(rèn)的支持JAR對(duì)應(yīng)的協(xié)議,因?yàn)镾pring Boot啟動(dòng)不僅僅需要JDK半身的JAR文件,還需要BOOT-INF/lib這個(gè)目錄下的文件。默認(rèn)實(shí)現(xiàn)無(wú)法將BOOT-INF/lib這個(gè)目錄當(dāng)作ClassPath,故需要替換實(shí)現(xiàn)。
判斷當(dāng)前的介質(zhì),是java -jar啟動(dòng),還是java org.springframework.boot.loader.JarLauncher啟動(dòng)。以便獲取對(duì)應(yīng)的ClassLoader。
獲取MANIFEST.MF文件中的Start-Class屬性,也就是我們自定義的主類(lèi)。通過(guò)第二步獲取的ClassLoader加載獲取到Class文件,通過(guò)反射調(diào)用main方法,啟動(dòng)應(yīng)用。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/75442.html
摘要:服務(wù)器相關(guān)配置啟動(dòng)類(lèi)資源目錄目錄靜態(tài)文件目錄端口號(hào)目錄目錄實(shí)現(xiàn)內(nèi)嵌服務(wù)器在上一章文章從零開(kāi)始實(shí)現(xiàn)一個(gè)簡(jiǎn)易的框架七實(shí)現(xiàn)已經(jīng)在文件中引入了依賴(lài),所以這里就不用引用了。 spring-boot的Starter 一個(gè)項(xiàng)目總是要有一個(gè)啟動(dòng)的地方,當(dāng)項(xiàng)目部署在tomcat中的時(shí)候,經(jīng)常就會(huì)用tomcat的startup.sh(startup.bat)的啟動(dòng)腳本來(lái)啟動(dòng)web項(xiàng)目 而在spring-b...
摘要:學(xué)習(xí)筆記使用很容易創(chuàng)建一個(gè)獨(dú)立運(yùn)行運(yùn)行內(nèi)嵌容器準(zhǔn)生產(chǎn)級(jí)別的基于框架的項(xiàng)目,使用你可以不用或者只需要很少的配置。異常消息如果這個(gè)錯(cuò)誤是由異常引起的。錯(cuò)誤發(fā)生時(shí)請(qǐng)求的路徑。 Spring-Boot 1.5 學(xué)習(xí)筆記 使用Spring Boot很容易創(chuàng)建一個(gè)獨(dú)立運(yùn)行(運(yùn)行jar,內(nèi)嵌Servlet容器)、準(zhǔn)生產(chǎn)級(jí)別的基于Spring框架的項(xiàng)目,使用Spring Boot你可以不用或者只需要很...
摘要:如下頁(yè)面模版的配置啟動(dòng)簡(jiǎn)單流程當(dāng)我們運(yùn)行的方法時(shí)調(diào)用靜態(tài)方法首先是實(shí)例化初始化的時(shí)候主要做主要做三件事根據(jù)下是否存在判斷是否要啟動(dòng)一個(gè)。將配置環(huán)境加入到監(jiān)聽(tīng)器對(duì)象中。方法將等重要組件與上下文對(duì)象關(guān)聯(lián)。自此的簡(jiǎn)單流程到此結(jié)束。 正文 說(shuō)springboot的啟動(dòng)流程當(dāng)然少不了springboot啟動(dòng)入口類(lèi) @SpringBootApplication public class Sprin...
摘要:先來(lái)看一下容器的文件中間一些操作省略這里用了多階段構(gòu)建容器,如果直接通過(guò)設(shè)置環(huán)境變量只會(huì)在后面一個(gè)階段生效,但是是在第一個(gè)階段執(zhí)行的,所以環(huán)境變量不能應(yīng)用到當(dāng)中。 在今年年初的時(shí)候,完成了自己的個(gè)Fame博客系統(tǒng)的實(shí)現(xiàn),當(dāng)時(shí)也做了一篇博文Spring-boot+Vue = Fame 寫(xiě)blog的一次小結(jié)作為記錄和介紹。從完成實(shí)現(xiàn)到現(xiàn)在,也斷斷續(xù)續(xù)的根據(jù)實(shí)際的使用情況進(jìn)行更新。 只不過(guò)每...
摘要:創(chuàng)建及準(zhǔn)備創(chuàng)建。目前已知關(guān)心這個(gè)事件的有要注意的是在這個(gè)階段,里只有,是的加載工作的起點(diǎn)。原因是注入這些回調(diào)接口本身沒(méi)有什么意義。在其構(gòu)造函數(shù)內(nèi)部間接的給注冊(cè)了幾個(gè)與相關(guān)注解的處理器。 相關(guān)代碼在: https://github.com/chanjarster/spring-boot-all-callbacks 注:本文基于spring-boot 1.4.1.RELEASE, spri...
閱讀 537·2023-04-25 14:26
閱讀 1292·2021-11-25 09:43
閱讀 3485·2021-09-22 15:25
閱讀 1454·2019-08-30 15:54
閱讀 528·2019-08-30 12:57
閱讀 773·2019-08-29 17:24
閱讀 3170·2019-08-28 18:13
閱讀 2691·2019-08-28 17:52