日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

SpringBoot java-jar命令行啟動(dòng)原理解析

瀏覽:19日期:2022-08-30 08:24:37

在spring boot里,很吸引人的一個(gè)特性是可以直接把應(yīng)用打包成為一個(gè)jar/war,然后這個(gè)jar/war是可以直接啟動(dòng)的,而不需要另外配置一個(gè)Web Server。那么spring boot如何啟動(dòng)的呢?今天我們就來(lái)一起探究一下它的原理。首先我們來(lái)創(chuàng)建一個(gè)基本的spring boot工程來(lái)幫助我們分析,本次spring boot版本為 2.2.5.RELEASE。

// SpringBootDemo.java@SpringBootApplicationpublic class SpringBootDemo { public static void main(String[] args) { SpringApplication.run(SpringBootDemo.class); }}

下面是pom依賴(lài):

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency></dependencies><build> <finalName>springboot-demo</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins></build>

創(chuàng)建完工程后,執(zhí)行maven的打包命令,會(huì)生成兩個(gè)jar文件:

springboot-demo.jarspringboot-demo.jar.original

其中springboot-demo.jar.original是默認(rèn)的maven-jar-plugin生成的包。springboot-demo.jar是spring boot maven插件生成的jar包,里面包含了應(yīng)用的依賴(lài),以及spring boot相關(guān)的類(lèi)。下面稱(chēng)之為executable jar或者fat jar。后者僅包含應(yīng)用編譯后的本地資源,而前者引入了相關(guān)的第三方依賴(lài),這點(diǎn)從文件大小也能看出。

SpringBoot java-jar命令行啟動(dòng)原理解析

圖1

關(guān)于executable jar,spring boot官方文檔中是這樣解釋的。

Executable jars (sometimes called “fat jars”) are archives containing your compiled classes along with all of the jar dependencies that your code needs to run.

Executable jar(有時(shí)稱(chēng)為“fat jars”)是包含您的已編譯類(lèi)以及代碼需要運(yùn)行的所有jar依賴(lài)項(xiàng)的歸檔文件。

Java does not provide any standard way to load nested jar files (that is, jar files that are themselves contained within a jar). This can be problematic if you need to distribute a self-contained application that can be run from the command line without unpacking.

Java沒(méi)有提供任何標(biāo)準(zhǔn)的方式來(lái)加載嵌套的jar文件(即,它們本身包含在jar中的jar文件)。如果您需要分發(fā)一個(gè)自包含的應(yīng)用程序,而該應(yīng)用程序可以從命令行運(yùn)行而無(wú)需解壓縮,則可能會(huì)出現(xiàn)問(wèn)題。

To solve this problem, many developers use “shaded” jars. A shaded jar packages all classes, from all jars, into a single “uber jar”. The problem with shaded jars is that it becomes hard to see which libraries are actually in your application. It can also be problematic if the same filename is used (but with different content) in multiple jars.

為了解決這個(gè)問(wèn)題,許多開(kāi)發(fā)人員使用 shaded jars。 一個(gè) shaded jar 將來(lái)自所有jar的所有類(lèi)打包到一個(gè) uber(超級(jí))jar 中。 shaded jars的問(wèn)題在于,很難查看應(yīng)用程序中實(shí)際包含哪些庫(kù)。 如果在多個(gè)jar中使用相同的文件名(但具有不同的內(nèi)容),也可能會(huì)產(chǎn)生問(wèn)題。

Spring Boot takes a different approach and lets you actually nest jars directly.

Spring Boot采用了另一種方法,實(shí)際上允許您直接嵌套jar。

簡(jiǎn)單來(lái)說(shuō),Java標(biāo)準(zhǔn)中是沒(méi)有來(lái)加載嵌套的jar文件,就是jar中的jar的方式的,為了解決這一問(wèn)題,很多開(kāi)發(fā)人員采用shaded jars,但是這種方式會(huì)有一些問(wèn)題,而spring boot采用了不同于shaded jars的另一種方式。

Executable Jar 文件結(jié)構(gòu)

那么spring boot具體是如何實(shí)現(xiàn)的呢?帶著這個(gè)疑問(wèn),先來(lái)查看spring boot打好的包的目錄結(jié)構(gòu)(不重要的省略掉):

SpringBoot java-jar命令行啟動(dòng)原理解析

圖6

可以發(fā)現(xiàn),文件目錄遵循了下面的規(guī)范:

Application classes should be placed in a nestedBOOT-INF/classesdirectory. Dependencies should be placed in a nested BOOT-INF/libdirectory.

應(yīng)用程序類(lèi)應(yīng)該放在嵌套的BOOT-INF/classes目錄中。依賴(lài)項(xiàng)應(yīng)該放在嵌套的BOOT-INF/lib目錄中。

我們通常在服務(wù)器中使用java -jar命令啟動(dòng)我們的應(yīng)用程序,在Java官方文檔是這樣描述的:

Executes a program encapsulated in a JAR file. The filename argument is the name of a JAR file with a manifest that contains a line in the form Main-Class:classname that defines the class with the public static void main(String[] args) method that serves as your application’s starting point.

執(zhí)行封裝在JAR文件中的程序。filename參數(shù)是具有清單的JAR文件的名稱(chēng),該清單包含Main-Class:classname形式的行,該行使用公共靜態(tài)void main(String [] args)方法定義該類(lèi),該方法充當(dāng)應(yīng)用程序的起點(diǎn)。

When you use the -jar option, the specified JAR file is the source of all user classes, and other class path settings are ignored.

使用-jar選項(xiàng)時(shí),指定的JAR文件是所有用戶(hù)類(lèi)的源,而其他類(lèi)路徑設(shè)置將被忽略。

簡(jiǎn)單說(shuō)就是,java -jar 命令引導(dǎo)的具體啟動(dòng)類(lèi)必須配置在清單文件 MANIFEST.MF 的 Main-Class 屬性中,該命令用來(lái)引導(dǎo)標(biāo)準(zhǔn)可執(zhí)行的jar文件,讀取的是 MANIFEST.MF文件的Main-Class 屬性值,Main-Class 也就是定義包含了main方法的類(lèi)代表了應(yīng)用程序執(zhí)行入口類(lèi)。

那么回過(guò)頭再去看下之前打包好、解壓之后的文件目錄,找到 /META-INF/MANIFEST.MF 文件,看下元數(shù)據(jù):

Manifest-Version: 1.0 Implementation-Title: spring-boot-demo Implementation-Version: 1.0-SNAPSHOT Start-Class: com.example.spring.boot.demo.SpringBootDemo Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Build-Jdk-Spec: 1.8 Spring-Boot-Version: 2.2.5.RELEASE Created-By: Maven Archiver 3.4.0 Main-Class: org.springframework.boot.loader.JarLauncher

可以看到Main-Class是org.springframework.boot.loader.JarLauncher,說(shuō)明項(xiàng)目的啟動(dòng)入口并不是我們自己定義的啟動(dòng)類(lèi),而是JarLauncher。而我們自己的項(xiàng)目引導(dǎo)類(lèi)com.example.spring.boot.demo.SpringBootDemo,定義在了Start-Class屬性中,這個(gè)屬性并不是Java標(biāo)準(zhǔn)的MANIFEST.MF文件屬性。

spring-boot-maven-plugin 打包過(guò)程

我們并沒(méi)有添加org.springframework.boot.loader下的這些類(lèi)的依賴(lài),那么它們是如何被打包在 FatJar 里面的呢?這就必須要提到spring-boot-maven-plugin插件的工作機(jī)制了 。對(duì)于每個(gè)新建的 spring boot工程,可以在其 pom.xml 文件中看到如下插件:

<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins></build>

這個(gè)是 SpringBoot 官方提供的用于打包 FatJar 的插件,org.springframework.boot.loader 下的類(lèi)其實(shí)就是通過(guò)這個(gè)插件打進(jìn)去的;

當(dāng)我們執(zhí)行package命令的時(shí)候會(huì)看到下面這樣的日志:

[INFO] --- spring-boot-maven-plugin:2.2.5.RELEASE:repackage (repackage) @ spring-boot-demo ---[INFO] Replacing main artifact with repackaged archive

repackage目標(biāo)對(duì)應(yīng)的將執(zhí)行到org.springframework.boot.maven.RepackageMojo#execute,該方法的主要邏輯是調(diào)用了org.springframework.boot.maven.RepackageMojo#repackage

// RepackageMojo.javaprivate void repackage() throws MojoExecutionException { // 獲取使用maven-jar-plugin生成的jar,最終的命名將加上.orignal后綴 Artifact source = getSourceArtifact(); // 最終文件,即Fat jar File target = getTargetFile(); // 獲取重新打包器,將重新打包成可執(zhí)行jar文件 Repackager repackager = getRepackager(source.getFile()); // 查找并過(guò)濾項(xiàng)目運(yùn)行時(shí)依賴(lài)的jar Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(), getFilters(getAdditionalFilters())); // 將artifacts轉(zhuǎn)換成libraries Libraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack, getLog()); try { // 提供Spring Boot啟動(dòng)腳本 LaunchScript launchScript = getLaunchScript(); // 執(zhí)行重新打包邏輯,生成最后fat jar repackager.repackage(target, libraries, launchScript); } catch (IOException ex) { throw new MojoExecutionException(ex.getMessage(), ex); } // 將source更新成 xxx.jar.orignal文件 updateArtifact(source, target, repackager.getBackupFile());}// 繼續(xù)跟蹤getRepackager這個(gè)方法,知道Repackager是如何生成的,也就大致能夠推測(cè)出內(nèi)在的打包邏輯。private Repackager getRepackager(File source) { Repackager repackager = new Repackager(source, this.layoutFactory); repackager.addMainClassTimeoutWarningListener(new LoggingMainClassTimeoutWarningListener()); // 設(shè)置main class的名稱(chēng),如果不指定的話則會(huì)查找第一個(gè)包含main方法的類(lèi), // repacke最后將會(huì)設(shè)置org.springframework.boot.loader.JarLauncher repackager.setMainClass(this.mainClass); if (this.layout != null) { getLog().info('Layout: ' + this.layout); repackager.setLayout(this.layout.layout()); } return repackager;}

repackager設(shè)置了 layout方法的返回對(duì)象,也就是org.springframework.boot.loader.tools.Layouts.Jar

/** * Executable JAR layout. */public static class Jar implements RepackagingLayout { @Override public String getLauncherClassName() { return 'org.springframework.boot.loader.JarLauncher'; } @Override public String getLibraryDestination(String libraryName, LibraryScope scope) { return 'BOOT-INF/lib/'; } @Override public String getClassesLocation() { return ''; } @Override public String getRepackagedClassesLocation() { return 'BOOT-INF/classes/'; } @Override public boolean isExecutable() { return true; }}

layout我們可以將之翻譯為文件布局,或者目錄布局,代碼一看清晰明了,同時(shí)我們又發(fā)現(xiàn)了定義在MANIFEST.MF 文件的Main-Class屬性org.springframework.boot.loader.JarLauncher了,看來(lái)我們的下面的重點(diǎn)就是研究一下這個(gè)JarLauncher了。

JarLauncher構(gòu)造過(guò)程

因?yàn)閛rg.springframework.boot.loader.JarLauncher的類(lèi)是在spring-boot-loader中的,關(guān)于spring-boot-loader,spring boot的github上是這樣介紹的:

Spring Boot Loader provides the secret sauce that allows you to build a single jar file that can be launched usingjava -jar. Generally you will not need to use spring-boot-loaderdirectly, but instead work with the Gradle or Maven plugin.

Spring Boot Loader提供了秘密工具,可讓您構(gòu)建可以使用java -jar啟動(dòng)的單個(gè)jar文件。通常,您不需要直接使用spring-boot-loader,而可以使用Gradle或Maven插件。

但是若想在IDEA中來(lái)看源碼,需要在pom文件中引入如下配置:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-loader</artifactId> <scope>provided</scope></dependency>

找到org.springframework.boot.loader.JarLauncher類(lèi)

// JarLauncher.javapublic class JarLauncher extends ExecutableArchiveLauncher { // BOOT-INF/classes/ static final String BOOT_INF_CLASSES = 'BOOT-INF/classes/'; // BOOT-INF/lib/ 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); } // main方法 public static void main(String[] args) throws Exception { new JarLauncher().launch(args); }}

可以發(fā)現(xiàn),JarLauncher定義了BOOT_INF_CLASSES和BOOT_INF_LIB兩個(gè)常量,正好就是前面我們解壓之后的兩個(gè)文件目錄。JarLauncher包含了一個(gè)main方法,作為應(yīng)用的啟動(dòng)入口。

從 main 來(lái)看,只是構(gòu)造了一個(gè) JarLauncher對(duì)象,然后執(zhí)行其 launch 方法 。再來(lái)看一下JarLauncher的繼承結(jié)構(gòu):

SpringBoot java-jar命令行啟動(dòng)原理解析

圖2

構(gòu)造JarLauncherd對(duì)象時(shí)會(huì)調(diào)用父類(lèi)ExecutableArchiveLauncher的構(gòu)造方法:

// ExecutableArchiveLauncher.javapublic ExecutableArchiveLauncher() { try { // 構(gòu)造 archive 對(duì)象 this.archive = createArchive(); } catch (Exception ex) { throw new IllegalStateException(ex); }}// 構(gòu)造 archive 對(duì)象protected final Archive createArchive() throws Exception { ProtectionDomain protectionDomain = getClass().getProtectionDomain(); CodeSource codeSource = protectionDomain.getCodeSource(); URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null; // 這里就是拿到當(dāng)前的 classpath 的絕對(duì)路徑 String path = (location != null) ? location.getSchemeSpecificPart() : null; if (path == null) { throw new IllegalStateException('Unable to determine code source archive'); } File root = new File(path); if (!root.exists()) { throw new IllegalStateException('Unable to determine code source archive from ' + root); } // 將構(gòu)造的archive 對(duì)象返回 return (root.isDirectory() ? new ExplodedArchive(root) : new JarFileArchive(root));}

Archive

這里又需要我們先來(lái)了解一下Archive相關(guān)的概念。

archive即歸檔文件,這個(gè)概念在linux下比較常見(jiàn) 通常就是一個(gè)tar/zip格式的壓縮包 jar是zip格式

public abstract class Archive { public abstract URL getUrl(); public String getMainClass(); public abstract Collection<Entry> getEntries(); public abstract List<Archive> getNestedArchives(EntryFilter filter);}

Archive是在spring boot里抽象出來(lái)的用來(lái)統(tǒng)一訪問(wèn)資源的接口。該接口有兩個(gè)實(shí)現(xiàn),分別是ExplodedArchive和JarFileArchive。前者是一個(gè)文件目錄,后者是一個(gè)jar,都是用來(lái)在文件目錄和jar中尋找資源的,這里看到JarLauncher既支持jar啟動(dòng),也支持文件系統(tǒng)啟動(dòng),實(shí)際上我們?cè)诮鈮汉蟮奈募夸浝飯?zhí)行 java org.springframework.boot.loader.JarLauncher 命令也是可以正常啟動(dòng)的。

SpringBoot java-jar命令行啟動(dòng)原理解析

圖3

在FatJar中,使用的是后者。Archive都有一個(gè)自己的URL,比如

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!

Archive類(lèi)還有一個(gè)getNestedArchives方法,下面還會(huì)用到這個(gè)方法,這個(gè)方法實(shí)際返回的是springboot-demo.jar/lib下面的jar的Archive列表。它們的URL是:

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-starter-web-2.2.5.RELEASE.jar!

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-starter-2.2.5.RELEASE.jar!

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-2.2.5.RELEASE.jar!

jar:file:/D:/java/workspace/spring-boot-bootstarp-demo/spring-boot-demo/target/springboot-demo.jar!/BOOT-INF/lib/spring-boot-autoconfigure-2.2.5.RELEASE.jar!/

省略......

launch()執(zhí)行流程

archive構(gòu)造完成后就該執(zhí)行JarLauncher的launch方法了,這個(gè)方法定義在了父類(lèi)的Launcher里:

// Launcher.javaprotected void launch(String[] args) throws Exception { /* * 利用 java.net.URLStreamHandler 的擴(kuò)展機(jī)制注冊(cè)了SpringBoot的自定義的可以解析嵌套jar的協(xié)議。 * 因?yàn)镾pringBoot FatJar除包含傳統(tǒng)Java Jar中的資源外還包含依賴(lài)的第三方Jar文件 * 當(dāng)SpringBoot FatJar被java -jar命令引導(dǎo)時(shí),其內(nèi)部的Jar文件是無(wú)法被JDK的默認(rèn)實(shí)現(xiàn) * sun.net.www.protocol.jar.Handler當(dāng)做classpath的,這就是SpringBoot的自定義協(xié)議的原因。 */ JarFile.registerUrlProtocolHandler(); // 通過(guò) classpath 來(lái)構(gòu)建一個(gè) ClassLoader ClassLoader classLoader = createClassLoader(getClassPathArchives()); // 1 launch(args, getMainClass(), classLoader); // 2}

重點(diǎn)關(guān)注下createClassLoader(getClassPathArchives()) 構(gòu)建ClassLoader的邏輯,首先調(diào)用getClassPathArchives()方法返回值作為參數(shù),該方法為抽象方法,具體實(shí)現(xiàn)在子類(lèi)ExecutableArchiveLauncher中:

// ExecutableArchiveLauncher.java@Overrideprotected List<Archive> getClassPathArchives() throws Exception { List<Archive> archives = new ArrayList<>(this.archive.getNestedArchives(this::isNestedArchive)); postProcessClassPathArchives(archives); return archives;}

該方法會(huì)執(zhí)行Archive接口定義的getNestedArchives方法返回的與指定過(guò)濾器匹配的條目的嵌套存檔列表。從上文可以發(fā)現(xiàn),這里的archive其實(shí)就是JarFileArchive ,傳入的過(guò)濾器是JarLauncher#isNestedArchive方法引用

// JarLauncher.java@Overrideprotected boolean isNestedArchive(Archive.Entry entry) { // entry是文件目錄時(shí),必須是我們自己的業(yè)務(wù)類(lèi)所在的目錄 BOOT-INF/classes/ if (entry.isDirectory()) { return entry.getName().equals(BOOT_INF_CLASSES); } // entry是Jar文件時(shí),需要在依賴(lài)的文件目錄 BOOT-INF/lib/下面 return entry.getName().startsWith(BOOT_INF_LIB);}

getClassPathArchives方法通過(guò)過(guò)濾器將BOOT-INF/classes/和BOOT-INF/lib/下的嵌套存檔作為L(zhǎng)ist<Archive>返回參數(shù)傳入createClassLoader方法中。

// Launcher.javaprotected ClassLoader createClassLoader(List<Archive> archives) throws Exception { List<URL> urls = new ArrayList<>(archives.size()); for (Archive archive : archives) { // 前面說(shuō)到,archive有一個(gè)自己的URL的,獲得archive的URL放到list中 urls.add(archive.getUrl()); } // 調(diào)用下面的重載方法 return createClassLoader(urls.toArray(new URL[0]));}// Launcher.javaprotected ClassLoader createClassLoader(URL[] urls) throws Exception { return new LaunchedURLClassLoader(urls, getClass().getClassLoader());}

createClassLoader()方法目的是為得到的URL們創(chuàng)建一個(gè)類(lèi)加載器 LaunchedURLClassLoader,構(gòu)造時(shí)傳入了當(dāng)前Launcher的類(lèi)加載器作為其父加載器,通常是系統(tǒng)類(lèi)加載器。下面重點(diǎn)看一下LaunchedURLClassLoader的構(gòu)造過(guò)程:

// LaunchedURLClassLoader.javapublic LaunchedURLClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent);}

LaunchedURLClassLoader是spring boot自己定義的類(lèi)加載器,繼承了JDK的URLClassLoader并重寫(xiě)了loadClass方法,也就是說(shuō)它修改了默認(rèn)的類(lèi)加載方式,定義了自己的類(lèi)加載規(guī)則,可以從前面得到的 List<Archive>中加載依賴(lài)包的class文件了 。

LaunchedURLClassLoader創(chuàng)建完成后,我們回到Launcher中,下一步就是執(zhí)行l(wèi)aunch的重載方法了。

// Launcher.javalaunch(args, getMainClass(), classLoader);

在此之前,會(huì)調(diào)用getMainClass方法并將其返回值作為參數(shù)。

getMainClass的實(shí)現(xiàn)在Launcher的子類(lèi)ExecutableArchiveLauncher中:

// ExecutableArchiveLauncher.java@Overrideprotected String getMainClass() throws Exception { // 從 archive 中拿到 Manifest文件 Manifest manifest = this.archive.getManifest(); String mainClass = null; if (manifest != null) { // 就是MANIFEST.MF 文件中定義的Start-Class屬性,也就是我們自己寫(xiě)的com.example.spring.boot.demo.SpringBootDemo這個(gè)類(lèi) mainClass = manifest.getMainAttributes().getValue('Start-Class'); } if (mainClass == null) { throw new IllegalStateException('No ’Start-Class’ manifest entry specified in ' + this); } // 返回mainClass return mainClass;}

得到mainClass后,執(zhí)行l(wèi)aunch的重載方法:

// Launcher.javaprotected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception { // 將自定義的LaunchedURLClassLoader設(shè)置為當(dāng)前線程上下文類(lèi)加載器 Thread.currentThread().setContextClassLoader(classLoader); // 構(gòu)建一個(gè) MainMethodRunner 實(shí)例對(duì)象來(lái)啟動(dòng)應(yīng)用 createMainMethodRunner(mainClass, args, classLoader).run();}// Launcher.javaprotected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) { return new MainMethodRunner(mainClass, args);}

MainMethodRunner對(duì)象構(gòu)建完成后,調(diào)用它的run方法:

// MainMethodRunner.javapublic void run() throws Exception { // 使用當(dāng)前線程上下文類(lèi)加載器也就是自定義的LaunchedURLClassLoader來(lái)加載我們自己寫(xiě)的com.example.spring.boot.demo.SpringBootDemo這個(gè)類(lèi) Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName); // 找到SpringBootDemo的main方法 Method mainMethod = mainClass.getDeclaredMethod('main', String[].class); // 最后,通過(guò)反射的方式調(diào)用main方法 mainMethod.invoke(null, new Object[] { this.args });}

至此,我們自己的main方法開(kāi)始被調(diào)用,所有我們自己的應(yīng)用程序類(lèi)文件均可通過(guò)/BOOT-INF/classes加載,所有依賴(lài)的第三方j(luò)ar均可通過(guò)/BOOT-INF/lib加載,然后就開(kāi)始了spring boot的啟動(dòng)流程了。

debug技巧

以上就是spring boot通過(guò)java -jar命令啟動(dòng)的原理了,了解了原理以后我們可不可以通過(guò)debug來(lái)進(jìn)一步加深一下理解呢?通常我們?cè)贗DEA里啟動(dòng)時(shí)是直接運(yùn)行main方法,因?yàn)橐蕾?lài)的Jar都讓IDEA放到classpath里了,所以spring boot直接啟動(dòng)就完事了,并不會(huì)通過(guò)上面的方式來(lái)啟動(dòng)。不過(guò)我們可以通過(guò)配置IDEA的 run/debug configurations 配置 JAR Application 來(lái)實(shí)現(xiàn)通過(guò)Jar方式啟動(dòng)。

SpringBoot java-jar命令行啟動(dòng)原理解析

圖4

當(dāng)我們做了以上設(shè)置后,就可以來(lái)方便的在IDEA里來(lái)dubug源碼了。

SpringBoot java-jar命令行啟動(dòng)原理解析

圖5小結(jié)

本文通過(guò)JarLauncher為切入點(diǎn),介紹了spring boot的java -jar的啟動(dòng)方式,闡述了JarLauncher啟動(dòng)的基本工作原理,同時(shí)簡(jiǎn)單介紹了相關(guān)的spring-boot-maven-plugin插件和Archive、LaunchedURLClassLoader等相關(guān)概念,希望能夠?qū)Υ蠹业睦斫庥兴鶐椭?/p>

到此這篇關(guān)于SpringBoot java-jar命令行啟動(dòng)原理解析的文章就介紹到這了,更多相關(guān)SpringBoot java-jar命令行啟動(dòng)內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Java
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
麻豆精品在线播放| 精品视频在线你懂得| 欧美少妇精品| 蜜桃视频一区二区三区| 亚洲+小说+欧美+激情+另类| 丝袜美腿亚洲一区二区图片| 亚洲成人精品| 中文在线а√在线8| 欧美成人午夜| 性欧美69xoxoxoxo| 99视频精品视频高清免费| 亚洲精品成a人ⅴ香蕉片| 国产美女精品视频免费播放软件| 国产精品99精品一区二区三区∴| 91亚洲无吗| 久久久久99| 激情91久久| 欧美黄色精品| 一本大道色婷婷在线| 午夜国产欧美理论在线播放 | 午夜一级久久| 国产日韩欧美三级| 激情五月色综合国产精品| 99成人在线| 久久美女性网| 最新亚洲国产| 欧美日韩一二三四| 久久av日韩| 98精品视频| 中文一区一区三区免费在线观 | 国产精品毛片久久久| 午夜精品一区二区三区国产| 欧美日韩午夜电影网| 亚洲欧美日本国产专区一区| 国产精品视频3p| 极品日韩av| 高清一区二区三区av| 一区二区日韩免费看| 久久99精品久久久野外观看| 好吊一区二区三区| 黄色精品视频| 国产日韩欧美三区| 日韩欧乱色一区二区三区在线| 女人av一区| 精品三级久久久| 日本国产亚洲| 美国欧美日韩国产在线播放| 人人精品亚洲| 樱桃成人精品视频在线播放| 免费看久久久| 日本中文字幕一区二区视频| 久久福利毛片| 久久不射中文字幕| 亚洲欧美日本国产专区一区| 国产精品女主播一区二区三区| 欧美成a人国产精品高清乱码在线观看片在线观看久 | 91福利精品在线观看| 综合一区在线| 亚洲精品伊人| 日韩精品a在线观看91| 日韩极品在线观看| 视频一区日韩| 久久国际精品| 亚洲欧美一级| 亚洲免费专区| 国产亚洲一区二区三区啪| 欧美一级一区| 麻豆高清免费国产一区| 久久久91麻豆精品国产一区| 久久精品人人| 99国产精品免费视频观看| www.com.cn成人| 亚洲精品va| 亚洲精品看片| 精品国产乱码久久久久久樱花| 精品国产免费人成网站| 亚洲精品一区二区在线看| 999在线观看精品免费不卡网站| 国产91精品对白在线播放| 99国产精品| 国产高清精品二区| 久久久久免费av| 中文字幕免费一区二区| 国产精品www.| 在线视频观看日韩| 日本精品另类| www.九色在线| 亚州精品视频| 91精品xxx在线观看| 91亚洲精品在看在线观看高清| 精品视频高潮| 日韩一区二区三区精品视频第3页| 精品视频在线你懂得| 亚洲特色特黄| 国产精品mm| 日韩午夜电影| 色一区二区三区四区| 久久最新视频| 国产黄色精品| 亚洲狼人精品一区二区三区| 国产不卡精品| 视频一区中文字幕精品| 99精品视频在线| 精品一区不卡| 日本在线一区二区三区| 尤物在线精品| 亚洲1234区| 国产一区2区| 日韩二区三区在线观看| 日韩亚洲精品在线| 国产91精品对白在线播放| 国产一区二区三区亚洲综合| 欧美私人啪啪vps| 视频一区日韩精品| 免费看的黄色欧美网站| 99精品综合| 国产精品伦理久久久久久| 国产精品久久久久久久久久久久久久久| 日韩一级精品| 久久在线电影| 99成人超碰| 日韩视频一区二区三区在线播放免费观看| 91日韩免费| 国产精品15p| 国产精品一区毛片| 国产精品亚洲片在线播放| 亚洲精品无播放器在线播放| 亚洲精品欧美| 日韩精品高清不卡| 日本中文字幕视频一区| 亚洲三区欧美一区国产二区| 99热精品在线| 亚洲青青久久| 国产欧美一区二区色老头| 国产精品最新| 91视频精品| 亚洲午夜精品久久久久久app| 91精品啪在线观看国产18| 青青青免费在线视频| 97se亚洲| 久久只有精品| 日本在线高清| 久久夜夜操妹子| 久久精品国产99久久| 欧美日韩国产传媒| 亚洲一区欧美激情| 日韩精品视频在线看| 久久精品国产99国产| 九九九精品视频| 久久精选视频| 亚洲综合图色| 蜜桃精品视频| 亚洲精品极品少妇16p| 亚洲深爱激情| 91精品麻豆| 国产高清久久| 国产亚洲精品美女久久 | 国产一区一一区高清不卡| 免费不卡中文字幕在线| 日韩毛片一区| 日韩影院二区| 亚洲日本免费电影| 精品国产精品国产偷麻豆| 模特精品在线| a天堂资源在线| 日韩精品一级中文字幕精品视频免费观看 | 午夜久久av| av成人国产| 久久女人天堂| 国产精品大片免费观看| 久久国产欧美日韩精品| 男女性色大片免费观看一区二区| 精品视频高潮| 国产精品2区| 日韩国产欧美在线播放| 亚洲免费观看高清完整版在线观| 亚洲一区二区免费在线观看| 免费久久精品视频| 日韩不卡视频在线观看| 日本不卡高清| 天海翼精品一区二区三区| 日韩精品亚洲专区| 蜜桃久久久久| 欧美xxxx中国| 精品一区二区三区免费看| 四虎8848精品成人免费网站| 日本中文字幕视频一区| 日韩二区三区在线观看| 亚洲一区二区三区久久久| 亚洲综合三区| 日本一不卡视频| 日本亚洲最大的色成网站www| 欧美日韩va| 日韩中文影院| 日韩精选在线| 精品不卡一区| 亚洲最新av| 国产精品99精品一区二区三区∴| 国产一区一一区高清不卡| 国产成人久久精品麻豆二区|