国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

Spring Boot如何通過(guò)java -jar啟動(dòng)

發(fā)布時(shí)間:2021-07-05 18:40 來(lái)源:腳本之家 閱讀:0 作者:小小工匠 欄目: 開(kāi)發(fā)技術(shù)

目錄

      Pre

      大家開(kāi)發(fā)的基于Spring Boot 的應用 ,jar形式, 發(fā)布的時(shí)候,絕大部分都是使用java -jar 啟動(dòng)。 得益于Spring Boot 的封裝 , 再也不用操心搭建tomcat等相關(guān)web容器le , 一切變得非常美好, 那SpringBoot是怎么做到的呢?

      引導

      新建工程 打包 啟動(dòng)

      我們新創(chuàng )建一個(gè)Spring Boot的工程

      其中打包的配置為

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

      先打包一下

      查看target目錄

      然后啟動(dòng)

      java -jar 干啥的

      我們先看看 java -jar 干了啥 ?

      在oracle官網(wǎng)找到了該命令的描述:

      If the -jar option is specified, its argument is the name of the JAR file containing class and resource files for the application. The startup class must be indicated by the Main-Class manifest header in its source code.

      使用-jar參數時(shí),后面的參數是的jar 【spring-0.0.1-SNAPSHOT.jar】,該jar文件中包含的是class和資源文件; 在manifest文件中有Main-Class的定義;Main-Class的源碼中指定了整個(gè)應用的啟動(dòng)類(lèi);

      簡(jiǎn)單來(lái)說(shuō): java -jar會(huì )去找jar中的manifest文件,去找到Main-Class對應的真正的啟動(dòng)類(lèi);

      那看看去吧

      咦 ,這個(gè)Main-Class 是Spring Boot 的。

      我們還看到有個(gè)Start Class

      官方文檔中,只提到過(guò)Main-Class ,并沒(méi)有提到Start-Class;
      Start-Class的值是com.artisan.spring.Application,這是我們的java代碼中的唯一類(lèi),包含main方法, 是能夠真正的應用啟動(dòng)類(lèi)

      所以問(wèn)題就來(lái)了:理論上看,執行java -jar命令時(shí)JarLauncher類(lèi)會(huì )被執行,但實(shí)際上是com.artisan.spring.Application被執行了,這其中發(fā)生了什么呢?why?

      打包插件

      事實(shí)上,Java沒(méi)有提供任何標準的方式來(lái)加載嵌套的jar文件 (jar中包含jar ,即Spring Boot 中的fat jar)

      Spring Boot 默認的打包插件如下:

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

      執行maven clean package之后,會(huì )生成兩個(gè)文件,剛才我們也看到了

      spring-boot-maven-plugin簡(jiǎn)介

      spring-boot-maven-plugin項目存在于spring-boot-tools目錄中。

      spring-boot-maven-plugin默認有5個(gè)goals:repackage、run、start、stop、build-info。在打包的時(shí)候默認使用的是repackage。

      spring-boot-maven-plugin的repackage能夠將mvn package生成的軟件包,再次打包為可執行的軟件包,并將mvn package生成的軟件包重命名為.original*

      spring-boot-maven-plugin的repackage在代碼層面調用了RepackageMojoexecute方法,而在該方法中又調用了repackage方法。

      private void repackage() throws MojoExecutionException {
         // maven生成的jar,最終的命名將加上.original后綴
         Artifact source = getSourceArtifact();
         // 最終為可執行jar,即fat jar
         File target = getTargetFile();
         // 獲取重新打包器,將maven生成的jar重新打包成可執行jar
         Repackager repackager = getRepackager(source.getFile());
         // 查找并過(guò)濾項目運行時(shí)依賴(lài)的jar
         Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(),
               getFilters(getAdditionalFilters()));
         // 將artifacts轉換成libraries
         Libraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack,
               getLog());
         try {
            // 獲得Spring Boot啟動(dòng)腳本
            LaunchScript launchScript = getLaunchScript();
            // 執行重新打包,生成fat jar
            repackager.repackage(target, libraries, launchScript);
         }catch (IOException ex) {
            throw new MojoExecutionException(ex.getMessage(), ex);
         }
         // 將maven生成的jar更新成.original文件
         updateArtifact(source, target, repackager.getBackupFile());
      }
      

      執行以上命令之后,便生成了打包結果對應的兩個(gè)文件。

      包結構

      下面針對文件的內容和結構進(jìn)行一探究竟。

      spring-0.0.1-SNAPSHOT.jar
      ├── META-INF
      │   └── maven(主要是pom文件)
      │   └── MANIFEST.MF
      ├── BOOT-INF
      │   ├── classes
      │   │   └── 應用程序類(lèi)
      │   └── lib
      │       └── 第三方依賴(lài)jar
      └── org
          └── springframework
              └── boot
                  └── loader
                      └── springboot啟動(dòng)程序
      

      META-INF內容

      Manifest-Version: 1.0
      Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
      Implementation-Title: spring
      Implementation-Version: 0.0.1-SNAPSHOT
      Spring-Boot-Layers-Index: BOOT-INF/layers.idx
      Start-Class: com.artisan.spring.Application
      Spring-Boot-Classes: BOOT-INF/classes/
      Spring-Boot-Lib: BOOT-INF/lib/
      Build-Jdk-Spec: 1.8
      Spring-Boot-Version: 2.4.1
      Created-By: Maven Jar Plugin 3.2.0
      Main-Class: org.springframework.boot.loader.JarLauncher
      • Main-Class:org.springframework.boot.loader.JarLauncher ,這個(gè)是jar啟動(dòng)的Main函數Start-Class: com.artisan.spring.Application,這個(gè)是我們應用自己的Main函數

      Archive的概念

      在繼續了解底層概念和原理之前,我們先來(lái)了解一下Archive的概念:

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

      SpringBoot抽象了Archive的概念,一個(gè)Archive可以是jar(JarFileArchive),可以是一個(gè)文件目錄(ExplodedArchive),可以抽象為統一訪(fǎng)問(wèn)資源的邏輯層

      關(guān)于Spring Boot中Archive的源碼如下:

      public interface Archive extends Iterable<Archive.Entry> {
          // 獲取該歸檔的url
          URL getUrl() throws MalformedURLException;
          // 獲取jar!/META-INF/MANIFEST.MF或[ArchiveDir]/META-INF/MANIFEST.MF
          Manifest getManifest() throws IOException;
          // 獲取jar!/BOOT-INF/lib/*.jar或[ArchiveDir]/BOOT-INF/lib/*.jar
          List<Archive> getNestedArchives(EntryFilter filter) throws IOException;
      }
      

      SpringBoot定義了一個(gè)接口用于描述資源,也就是org.springframework.boot.loader.archive.Archive。

      該接口有兩個(gè)實(shí)現,分別是

      • org.springframework.boot.loader.archive.ExplodedArchive
      • org.springframework.boot.loader.archive.JarFileArchive。

      前者用于在文件夾目錄下尋找資源,后者用于在jar包環(huán)境下尋找資源。而在SpringBoot打包的fatJar中,則是使用后者JarFileArchive

      JarFile

      JarFile:對jar包的封裝,每個(gè)JarFileArchive都會(huì )對應一個(gè)JarFile。

      JarFile被構造的時(shí)候會(huì )解析內部結構,去獲取jar包里的各個(gè)文件或文件夾,這些文件或文件夾會(huì )被封裝到Entry中,也存儲在JarFileArchive中。如果Entry是個(gè)jar,會(huì )解析成JarFileArchive。

      比如一個(gè)JarFileArchive對應的URL為:

      jar:file:/Users/format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/

      它對應的JarFile為:

      /Users/format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar

      這個(gè)JarFile有很多Entry,比如:

      META-INF/
      META-INF/MANIFEST.MF
      spring/
      spring/study/
      ....
      spring/study/executablejar/ExecutableJarApplication.class
      lib/spring-boot-starter-1.3.5.RELEASE.jar
      lib/spring-boot-1.3.5.RELEASE.jar
      ...

      JarFileArchive內部的一些依賴(lài)jar對應的URL(SpringBoot使用org.springframework.boot.loader.jar.Handler處理器來(lái)處理這些URL):

      jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-starter-web-1.3.5.RELEASE.jar!/
      
      jar:file:/Users/Format/Develop/gitrepository/springboot-analysis/springboot-executable-jar/target/executable-jar-1.0-SNAPSHOT.jar!/lib/spring-boot-loader-1.3.5.RELEASE.jar!/org/springframework/boot/loader/JarLauncher.class
      

      我們看到如果有jar包中包含jar,或者jar包中包含jar包里面的class文件,那么會(huì )使用 !/ 分隔開(kāi),這種方式只有org.springframework.boot.loader.jar.Handler能處理,它是SpringBoot內部擴展出來(lái)的一種URL協(xié)議。

      JarLauncher工作流程

      從MANIFEST.MF可以看到Main函數是JarLauncher,下面來(lái)分析它的工作流程。JarLauncher類(lèi)的繼承結構是:

      class JarLauncher extends ExecutableArchiveLauncher
      class ExecutableArchiveLauncher extends Launcher
      

      Launcher for JAR based archives. This launcher assumes that dependency jars are included inside a /BOOT-INF/lib directory and that application classes are included inside a /BOOT-INF/classes directory.

      什么意思呢?

      按照定義,JarLauncher可以加載內部/BOOT-INF/lib下的jar及/BOOT-INF/classes下的應用class。

      public class JarLauncher extends ExecutableArchiveLauncher {
          public JarLauncher() {}
          public static void main(String[] args) throws Exception {
              new JarLauncher().launch(args);
          }
      }
      

      其主入口新建了JarLauncher并調用父類(lèi)Launcher中的launch方法啟動(dòng)程序。在創(chuàng )建JarLauncher時(shí),父類(lèi)ExecutableArchiveLauncher找到自己所在的jar,并創(chuàng )建archive。

      JarLauncher繼承于org.springframework.boot.loader.ExecutableArchiveLauncher。該類(lèi)的無(wú)參構造方法最主要的功能就是構建了當前main方法所在的FatJar的JarFileArchive對象。

      下面來(lái)看launch方法。該方法主要是做了2個(gè)事情:

      (1)以FatJar為file作為入參,構造JarFileArchive對象。獲取其中所有的資源目標,取得其Url,將這些URL作為參數,構建了一個(gè)URLClassLoader。

      (2)以第一步構建的ClassLoader加載MANIFEST.MF文件中Start-Class指向的業(yè)務(wù)類(lèi),并且執行靜態(tài)方法main。進(jìn)而啟動(dòng)整個(gè)程序。

      public abstract class ExecutableArchiveLauncher extends Launcher {
          private final Archive archive;
          public ExecutableArchiveLauncher() {
              try {
                  // 找到自己所在的jar,并創(chuàng  )建Archive
                  this.archive = createArchive();
              }
              catch (Exception ex) {
                  throw new IllegalStateException(ex);
              }
          }
      }
       
       
      public abstract class Launcher {
          protected final Archive createArchive() throws Exception {
              ProtectionDomain protectionDomain = getClass().getProtectionDomain();
              CodeSource codeSource = protectionDomain.getCodeSource();
              URI location = (codeSource == null ? null : codeSource.getLocation().toURI());
              String path = (location == null ? null : location.getSchemeSpecificPart());
              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);
              }
              return (root.isDirectory() ? new ExplodedArchive(root)
                      : new JarFileArchive(root));
          }
      }
      

      在Launcher的launch方法中,通過(guò)以上archive的getNestedArchives方法找到/BOOT-INF/lib下所有jar及/BOOT-INF/classes目錄所對應的archive,通過(guò)這些archives的url生成LaunchedURLClassLoader,并將其設置為線(xiàn)程上下文類(lèi)加載器,啟動(dòng)應用。

      至此,才執行我們應用程序主入口類(lèi)的main方法,所有應用程序類(lèi)文件均可通過(guò)/BOOT-INF/classes加載,所有依賴(lài)的第三方j(luò )ar均可通過(guò)/BOOT-INF/lib加載。

      小結

      • JarLauncher通過(guò)加載BOOT-INF/classes目錄及BOOT-INF/lib目錄下jar文件,實(shí)現了fat jar的啟動(dòng)。
      • SpringBoot通過(guò)擴展JarFile、JarURLConnection及URLStreamHandler,實(shí)現了jar in jar中資源的加載。
      • SpringBoot通過(guò)擴展URLClassLoader–LauncherURLClassLoader,實(shí)現了jar in jar中class文件的加載。
      • WarLauncher通過(guò)加載WEB-INF/classes目錄及WEB-INF/lib和WEB-INF/lib-provided目錄下的jar文件,實(shí)現了war文件的直接啟動(dòng)及web容器中的啟動(dòng)。

      通過(guò)spring-boot-plugin 生成了MANIFEST.MF , main-class 指定運行java -jar的主程序把依賴(lài)的jar文件 打包在fat jar.

      到此這篇關(guān)于Spring Boot如何通過(guò)java -jar啟動(dòng)的文章就介紹到這了,更多相關(guān)SpringBoot java -jar啟動(dòng)內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

      免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

      免费看AV毛片一区二区三区| 少妇无码AV无码专区线| 国产麻豆MD传媒视频| 美女又大又黄www免费网站| 国产成人AV在线免播放观看 | 两个奶头被吃高潮视频|