亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Spring-Boot啟動之前做了哪些事?

發布時間:2020-07-12 19:48:22 來源:網絡 閱讀:161 作者:wx5d2fe93fcf80d 欄目:編程語言

Spring Boot Jar文件探究

初始化一個Spring 應用,添加如下依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.fxipp.spring</groupId>
    <artifactId>first-app-by-gui</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>first-app-by-gui</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

</project>

執行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步

  1. 擴展JAR協議
    1. JDK默認支持file、http、jar等協議,所以JDK內部有默認的實現,位于sun.net.www.protocol包下。
    2. JarFile.registerUrlProtocolHandler();這個方法將org.springframework.boot.loader包下對應的JAR協議實現,覆蓋原有的JAR實現。
    3. 因為原有的JAR實現,ClassPath是我們自己配置環境變量的時候制定的,不是BOOT-INF/lib
  2. 創建一個classloader,用于加載JarLauncher類,因為jar包可能會被解壓,解壓前和解壓后的的ClassLoader是不同的。
  3. 調用launch方法,將參數傳遞。
    1. args是我們自己指定的參數。
    2. getMainClass()是獲取MANIFEST.MF文件里面Statr-Class屬性,也就是獲取我們自定義主類的Class 文件地址。
    3. 傳遞推出的類加載器

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方法分析:

  1. 將ClassLoader放入當前線程里面的ClassLoader里面
  2. 創建MainMethodRunner對象,調用里面的run()方法。
    1. run()方法先獲取到之前設定的ClassLoader。
    2. 利用ClassLoader加載Start-Class之類的類,也就是我們自己的主類。
    3. 獲取主類里面的main方法,通過反射執行。

總結

通過分析,我們可以看出,Spring Boot Loader在調用我們自己的主類之前,主要做了三件事

  1. 擴展JDK默認的支持JAR對應的協議,因為Spring Boot啟動不僅僅需要JDK半身的JAR文件,還需要BOOT-INF/lib這個目錄下的文件。默認實現無法將BOOT-INF/lib這個目錄當作ClassPath,故需要替換實現。
  2. 判斷當前的介質,是java -jar啟動,還是java org.springframework.boot.loader.JarLauncher啟動。以便獲取對應的ClassLoader。
  3. 獲取MANIFEST.MF文件中的Start-Class屬性,也就是我們自定義的主類。通過第二步獲取的ClassLoader加載獲取到Class文件,通過反射調用main方法,啟動應用。
向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

志丹县| 平顺县| 晋中市| 三门县| 石渠县| 昆明市| 南宁市| 开平市| 如皋市| 左贡县| 防城港市| 遂溪县| 涟水县| 克拉玛依市| 阿瓦提县| 渑池县| 法库县| 崇信县| 上林县| 宁德市| 仙桃市| 阿拉善左旗| 安西县| 潮安县| 扶沟县| 天津市| 闸北区| 灌阳县| 陆川县| 印江| 腾冲县| 理塘县| 远安县| 德惠市| 鞍山市| 柘城县| 南涧| 瑞安市| 娄烦县| 崇州市| 沁源县|