您好,登錄后才能下訂單哦!
這篇文章主要介紹了SpringBoot的jar包怎么啟動,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
使用過SprongBoot
打過jar
包的都應該知道,目標文件一般都會生成兩個文件,一個是以.jar
的包,一個是.jar.original
文件。那么使用SpringBoot
會打出兩個包,而.jar.original
的作用是什么呢?還有就是java -jar
是如何將一個SpringBoot
項目啟動,之間都進行了那些的操作?
其實.jar.original
是maven
在SpringBoot
重新打包之前的原始jar
包,內部只包含了項目的用戶類,不包含其他的依賴jar
包,生成之后,SpringBoot
重新打包之后,最后生成.jar
包,內部包含了原始jar
包以及其他的引用依賴。以下提及的jar
包都是SpringBoot
二次加工打的包。
SpringBoot
打出的jar
包,可以直接通過解壓的方式查看內部的構造。一般情況下有三個目錄。
BOOT-INF
:這個文件夾下有兩個文件夾classes
用來存放用戶類,也就是原始jar.original
里的類;還有一個是lib
,就是這個原始jar.original
引用的依賴。
META-INF
:這里是通過java -jar
啟動的入口信息,記錄了入口類的位置等信息。
org
:Springboot loader
的代碼,通過它來啟動。
這里主要介紹一下/BOOT-INF/MANIFEST.MF
文件
Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: cn.com.springboot.center.AuthEenterBootstrap
Main-Class
:記錄了java -jar
的啟動入口,當使用該命令啟動時就會調用這個入口類的main
方法,顯然可以看出,Springboot
轉移了啟動的入口,不是用戶編寫的xxx.xxx.BootStrap
的那個入口類。
Start-Class
:記錄了用戶編寫的xxx.xxx.BootStrap
的那個入口類,當內嵌的jar
包加載完成之后,會使用LaunchedURLClassLoader
線程加載類來加載這個用戶編寫的入口類。
3.1.1 Archive
歸檔文件接口,實現迭代器接口,它有兩個子類,一個是JarFileArchive
對jar
包文件使用,提供了返回這個jar
文件對應的url
、或者這個jar
文件的MANIFEST
文件數據信息等操作。是ExplodedArchive
是文件目錄的使用也有獲取這個目錄url
的方法,以及獲取這個目錄下的所有Archive
文件方法。
3.1.2 Launcher
啟動程序的基類,這邊最后是通過JarLauncher#main()
來啟動。ExecutableArchiveLauncher
是抽象類,提供了獲取Start-Class
類路徑的方法,以及是否還有內嵌對應文件的判斷方法和獲取到內嵌對應文件集合的后置處理方法的抽象,由子類JarLauncher
和WarLauncher
自行實現。
3.1.3 Spring.loader下的JarFile和JarEntry
jarFile
繼承于jar.util.jar.JarFile
,JarEntry
繼承于java.util.jar.JarEntry
,對原始的一些方法進行重寫覆蓋。每一個JarFileArchive
都擁有一個JarFile
方法,用于存儲這個jar
包對應的文件,而每一個JarFile
都有一個JarFileEntries
,JarFileEntries
是一個迭代器。總的來說,在解析jar
包時,會將jar
包內的文件封裝成JarEntry
對象后由JarFile
對象保存文件列表的迭代器。所以JarFileArchive
和JarFileEntries
之間是通過JarFile
連接,二者都可以獲取到JarFile
對象。
從MANIFEST.MF
文件中的Main-class
指向入口開始。
創建JarLauncher
并且通過它的launch()
方法開始加載jar
包內部信息。
public static void main(String[] args) throws Exception { new JarLauncher().launch(args); }
JarLauncher
的空構造方法時一個空實現,剛開始看的時候還懵了一下,以為是在后續的操作中去加載的文件,其實不然,在創建時由父類ExecutableArchiveLauncher
的構造方法去加載的文件。
加載為歸檔文件對象:
this.archive = createArchive();
具體的加載方法:判斷路徑是否是一個文件夾,是則返回ExplodedArchive
對象,否則返回JarFileArchive
進入JarFileArchive
類:通過這個new
方法創建JarFile
對象
public class JarFileArchive implements Archive { public JarFileArchive(File file, URL url) throws IOException { this(new JarFile(file)); this.url = url; } }
進入到JarFile
方法:通過RandomAccessDataFile
讀取文件的內容,并傳遞給本類中的方法進行具體的解析。
public class JarFile extends java.util.jar.JarFile { public JarFile(File file) throws IOException { this(new RandomAccessDataFile(file)); } }
進入jarLauncher
的launch
方法:注冊URL
協議的處理器,沒有指定時,默認指向org.springframework.boot.loader
包路徑,獲取類路徑下的歸檔文件Archive
并通過這些歸檔文件的URL
,創建線程上下文類加載器,使用類加載器和用戶編寫的啟動入口類,通過反射調用它的main
方法。
protected void launch(String[] args) throws Exception { JarFile.registerUrlProtocolHandler(); ClassLoader classLoader = createClassLoader(getClassPathArchives()); launch(args, getMainClass(), classLoader); }
JarLauncher
的getClassPathArchives
是在ExecutableArchiveLauncher
中實現:獲取歸檔文件中滿足EntryFilterg
過濾器的項,isNestedArchive
方法由具體的之類實現。獲取到當前歸檔文件下的所有子歸檔文件之后的后置操作,是一個擴展點。在JarLauncher
中是一個空實現。
JarLauncher
的具體實現,這里通過判斷是否在BOOT-INF/lib/
包下返回true
也就是說只會把jar
包下的BOOT-INF/lib/
下的文件加載為Archive
對象
protected boolean isNestedArchive(Archive.Entry entry) { if (entry.isDirectory()) { return entry.getName().equals(BOOT_INF_CLASSES); } return entry.getName().startsWith(BOOT_INF_LIB); }
JarFileArchive
的getNestedArchives
方法:若匹配器匹配到則獲取內嵌歸檔文件。
具體的獲取內嵌歸檔文件邏輯:根據具體的Entry
對象,創建JarFile
對象并封裝成歸檔文件對象后返回。
protected Archive getNestedArchive(Entry entry) throws IOException { try { JarFile jarFile = this.jarFile.getNestedJarFile(jarEntry); return new JarFileArchive(jarFile); } }
獲取到參數entry
對應的RandomAccessData
對象,這里根據springboot
擴展的url
協議,在父路徑的基礎上添加!/
來標記子包。
private JarFile createJarFileFromFileEntry(JarEntry entry) throws IOException { RandomAccessData entryData = this.entries.getEntryData(entry.getName()); return new JarFile(this.rootFile, this.pathFromRoot + "!/" + entry.getName(), entryData, JarFileType.NESTED_JAR); }
到這基本上讀取jar
內部信息,加載為對應歸檔文件對象的大概過程已經講完了,接下來分析一下在獲取到了整個jar
的歸檔文件對象后的處理。
通過歸檔文件對象列表,獲取對應的url
信息,并通過url
信息創建LaunchedURLClassLoader
protected ClassLoader createClassLoader(List<Archive> archives) { List<URL> urls = new ArrayList<URL>(archives.size()); for (Archive archive : archives) { urls.add(archive.getUrl()); } return createClassLoader(urls.toArray(new URL[urls.size()])); }
獲取到對應的LaunchedUrlClassLoader
類加載器之后,設置線程的上下文類加載器為該加載器。根據MANIFI.MF
文件中的start-classs
信息創建項目啟動入口主類對象,并通過返回對象的run
方法啟動
protected void launch(String[] args, String mainClass, ClassLoader classLoader) { Thread.currentThread().setContextClassLoader(classLoader); createMainMethodRunner(mainClass, args, classLoader).run(); }
進入MainMethodRunner
的run
方法:先通過當前線程獲取到main
入口類,然后通過反射調用啟動項目啟動類的main
方法
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 }); }
最后來說一下這個LaunchedURLClassLoader
,它繼承于URLClassLoader
,并重寫了loadClass
方法
LaunchedClassLoader
的loadClass
方法:調用父類loadClass
方法,走正常委派流程,最終會被LaunchURLClassLoader
加載。
@Override protected Class<?> loadClass(String name, boolean resolve){ try { try { definePackageIfNecessary(name); } return super.loadClass(name, resolve); } }
進入URLClassLoader
中根據springboot
解析進行解析。根據名稱將路徑轉化為以.class
結尾的/
分隔的格式。通過UrlClassPath
對象根據路徑獲取資源類文件
new PrivilegedExceptionAction<Class<?>>() { public Class<?> run() throws ClassNotFoundException { String path = name.replace('.', '/').concat(".class"); Resource res = ucp.getResource(path, false); if (res != null) { try { return defineClass(name, res); } } } }
感謝你能夠認真閱讀完這篇文章,希望小編分享的“SpringBoot的jar包怎么啟動”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。