摘要:也就是說,可以執行成功,是因為知道了的路徑,說明在啟動調用之前,指定了的位置。判斷當前的介質,是啟動,還是啟動。
Spring-Boot啟動之前做了哪些事? Spring Boot Jar文件探究
初始化一個Spring 應用,添加如下依賴
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
執行mvn package命令打包,查看jar包的目錄結構
. ├── 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
文件結構比較復雜,解釋一下
BOOT-INF/classes: 存放應用編譯后的class文件;
BOOT-INF/lib:class path目錄, 存放應用依賴的jar包;
META-INF: 存放應用的元信息,如MANIFEST.MF文件;
org:存放Spring Boot自身的class文件;
Jar文件的執行器: 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
里面記錄了應用的元信息,Spring的版本,應用的版本,Maven的版本,Main-Class等信息。不難發現,MainClass指向的是org.springframework.boot.loader.JarLauncher(以下簡稱JarLauncher),而不是我們自己編寫的com.fxipp.spring.FirstAppByGuiApplication。
JarLauncher從名字看出是一個jar的執行器,他的class文件位于org.springframework.boot.loader目錄下,可見它是Spring自身的class文件。
JarLauncher的GAV org.springframework.boot:spring-boot-loader:2.1.6.RELEASE
通常情況下,他會在spring-boot-starter-parent引入到應用中,既然main-class指向到是JarLauncher,那我們也可以直接執行java org.springframework.boot.loader.JarLauncher,也可以啟動Spring項目的。
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)
既然可以執行,那就說明了,JarLauncher這個類才是Spring項目真正的入口。如果我們執行自己寫的com.fxipp.spring.FirstAppByGuiApplication會怎么樣?
? 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
啟動報錯,原因是找不到org.springframework.boot.SpringApplication這個類,說白了就是沒有指定Class Path,Spring Boot應用的Class Path目錄是BOOT-INF/lib。
也就是說,JarLauncher可以執行成功,是因為Spring Boot知道了Class Path的路徑,說明JarLauncher在啟動調用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:這個類對對象,代編jar包中的資源文件。
isNestedArchive方法判斷entry對象是不是位于jar包內,如果在jar內部,返回true。如果不在jar包里面,也就是我們解壓了jar包,返回false。
重點看launch(String[])方法
protected void launch(String[] args) throws Exception { // 1 JarFile.registerUrlProtocolHandler(); // 2 ClassLoader classLoader = createClassLoader(getClassPathArchives()); // 3 launch(args, getMainClass(), classLoader); }
這個方法一共3步
擴展JAR協議
JDK默認支持file、http、jar等協議,所以JDK內部有默認的實現,位于sun.net.www.protocol包下。
JarFile.registerUrlProtocolHandler();這個方法將org.springframework.boot.loader包下對應的JAR協議實現,覆蓋原有的JAR實現。
因為原有的JAR實現,ClassPath是我們自己配置環境變量的時候制定的,不是BOOT-INF/lib。
創建一個classloader,用于加載JarLauncher類,因為jar包可能會被解壓,解壓前和解壓后的的ClassLoader是不同的。
調用launch方法,將參數傳遞。
args是我們自己指定的參數。
getMainClass()是獲取MANIFEST.MF文件里面Statr-Class屬性,也就是獲取我們自定義主類的Class 文件地址。
傳遞推出的類加載器
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放入當前線程里面的ClassLoader里面
創建MainMethodRunner對象,調用里面的run()方法。
run()方法先獲取到之前設定的ClassLoader。
利用ClassLoader加載Start-Class之類的類,也就是我們自己的主類。
獲取主類里面的main方法,通過反射執行。
總結通過分析,我們可以看出,Spring Boot Loader在調用我們自己的主類之前,主要做了三件事
擴展JDK默認的支持JAR對應的協議,因為Spring Boot啟動不僅僅需要JDK半身的JAR文件,還需要BOOT-INF/lib這個目錄下的文件。默認實現無法將BOOT-INF/lib這個目錄當作ClassPath,故需要替換實現。
判斷當前的介質,是java -jar啟動,還是java org.springframework.boot.loader.JarLauncher啟動。以便獲取對應的ClassLoader。
獲取MANIFEST.MF文件中的Start-Class屬性,也就是我們自定義的主類。通過第二步獲取的ClassLoader加載獲取到Class文件,通過反射調用main方法,啟動應用。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/75442.html
摘要:服務器相關配置啟動類資源目錄目錄靜態文件目錄端口號目錄目錄實現內嵌服務器在上一章文章從零開始實現一個簡易的框架七實現已經在文件中引入了依賴,所以這里就不用引用了。 spring-boot的Starter 一個項目總是要有一個啟動的地方,當項目部署在tomcat中的時候,經常就會用tomcat的startup.sh(startup.bat)的啟動腳本來啟動web項目 而在spring-b...
摘要:學習筆記使用很容易創建一個獨立運行運行內嵌容器準生產級別的基于框架的項目,使用你可以不用或者只需要很少的配置。異常消息如果這個錯誤是由異常引起的。錯誤發生時請求的路徑。 Spring-Boot 1.5 學習筆記 使用Spring Boot很容易創建一個獨立運行(運行jar,內嵌Servlet容器)、準生產級別的基于Spring框架的項目,使用Spring Boot你可以不用或者只需要很...
摘要:如下頁面模版的配置啟動簡單流程當我們運行的方法時調用靜態方法首先是實例化初始化的時候主要做主要做三件事根據下是否存在判斷是否要啟動一個。將配置環境加入到監聽器對象中。方法將等重要組件與上下文對象關聯。自此的簡單流程到此結束。 正文 說springboot的啟動流程當然少不了springboot啟動入口類 @SpringBootApplication public class Sprin...
摘要:先來看一下容器的文件中間一些操作省略這里用了多階段構建容器,如果直接通過設置環境變量只會在后面一個階段生效,但是是在第一個階段執行的,所以環境變量不能應用到當中。 在今年年初的時候,完成了自己的個Fame博客系統的實現,當時也做了一篇博文Spring-boot+Vue = Fame 寫blog的一次小結作為記錄和介紹。從完成實現到現在,也斷斷續續的根據實際的使用情況進行更新。 只不過每...
摘要:創建及準備創建。目前已知關心這個事件的有要注意的是在這個階段,里只有,是的加載工作的起點。原因是注入這些回調接口本身沒有什么意義。在其構造函數內部間接的給注冊了幾個與相關注解的處理器。 相關代碼在: https://github.com/chanjarster/spring-boot-all-callbacks 注:本文基于spring-boot 1.4.1.RELEASE, spri...
閱讀 529·2023-04-25 14:26
閱讀 1285·2021-11-25 09:43
閱讀 3476·2021-09-22 15:25
閱讀 1447·2019-08-30 15:54
閱讀 520·2019-08-30 12:57
閱讀 765·2019-08-29 17:24
閱讀 3166·2019-08-28 18:13
閱讀 2672·2019-08-28 17:52