SpringBoot startup principle

[TOC]

background

This article explores how SpringBoot works. SpringBoot also inserts the dependency package into the final Jar at packaging time, turning it into a runnable FatJar. So it’s going to form a Jar in Jar structure. By default, the ClassLoader provided by the JDK can only recognize class files in jars and load class files in other Jar packages in the classpath. The JAR package inside the JAR package cannot be loaded.

Reserve knowledge

URLStreamHandler

Urls are often used to describe resources in Java. The URL has a method to open the link java.net.URL#openConnection(). Because the URL is used to express a variety of resources, open resources specific actions are done by java.net.URLStreamHandler subclasses of this class. Depending on the protocol, there are different handler implementations. The JDK has quite a few built-in handler implementations for different protocols. Such as JAR, file, HTTP, and so on. Inside the URL is a static HashTable property that holds the mapping of discovered protocols and handler instances.

There are three ways to get URLStreamHandler

  1. implementationURLStreamHandlerFactoryInterface, by methodURL.setURLStreamHandlerFactorySettings. This property is static and can only be set once.
  2. Directly provideURLStreamHandlerClass as one of the entries to the URL’s constructor. But there are fixed specification requirements in the JVM:
    1. The subclass name must be Handler, and the last-level package name must be the protocol name. For example, if the Http protocol implementation is customized, the class name must be XX.http.handler
    2. This is required when the JVM startsjava.protocol.handler.pkgsSystem property, if there are multiple implementation classes, then separated by | in the middle. Because the JVM gets the package name prefix from this property when it tries to find a Handler, it ends up using itPacket name prefix Protocol name. Handler, the use ofClass.forNameMethod attempts to initialize the class, and if initialization succeeds, the implementation of the class is used as the protocol implementation.

Archive

SpringBoot defines an interface that is used to describe resources, namely org. Springframework.. The boot loader. The archive. The archive. This interface has two implementations, Respectively is org. Springframework. Boot. Loader. Archive. ExplodedArchive and org., springframework.. The boot loader. Archive. JarFileArchive. The former is used to find resources in folder directories and the latter is used to find resources in jar package environments. In fatJars packaged by SpringBoot, the latter is used.

packaging

SpringBoot uses plug-ins

<plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.tccdemo.Eureka</mainClass>
                </configuration>
            </plugin>
Copy the code

The packed file layout is as follows:

  1. The program in the boot-INF folder compiles the class and dependent JAR packages
  2. The org directory hosts the boot packages for SpringBoot.

Take a look at the description file manifest.mf

Manifest-version: 1.0 implementation-title: Eureka implementation-version: 1.0-snapshot built-in: Administrator implementation-vendor-id: com.tccdemo spring-boot-version: 2.0.2.RELEASE main-class: org.springframework.boot.loader.JarLauncher Start-Class: com.tccdemo.Eureka Spring-Boot-Classes: Maven 3.6.1 build-inf /classes/ spring-boot-inf/Lib: boot-INF/Lib/created-by: Apache Maven 3.6.1 build-JDK: 1.8.0 comes with _201 Implementation - the URL: http://www.example.comCopy the code

The most obvious thing is that the startup class of the program is not the startup class of our project, but the JarLauncher of SpringBoot. We’ll take a closer look at what this class does.

SpringBoot start

Let’s start with the startup method

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

Copy the code

JarLauncher inheritance in org. Springframework.. The boot loader. ExecutableArchiveLauncher. The primary function of the no-argument constructor of this class is to build the JarFileArchive object of the FatJar in which the main method is currently located. Let’s look at the launch method. This method mainly does two things:

  1. Construct the JarFileArchive object with FatJar as file as the input parameter. Get all the resource targets, get their urls, take these urls as parameters, build a URLClassLoader.
  2. Load with the ClassLoader built in the first stepMANIFEST.MFIn the fileStart-ClassTo the business class, and executes the static method main. Then start the whole program.

. By the method of static org. Springframework. The boot loader. JarLauncher# main can start the whole process smoothly. The key here is that SpringBoot’s custom classLoader is able to recognize resources in fatJars, including projects that compile classes in the specified directory and project-dependent jars in the instruction directory. The default AppClassLoader used by the JDK to load applications can only load class files from the root of the JAR and does not support the jar in JAR format.

To achieve this goal, SpringBoot first customizes content reading from support JAR in jar, that is, support multiple! / delimiter url path. SpringBoot customizes the following two aspects:

  1. Implemented ajava.net.URLStreamHandlerA subclass oforg.springframework.boot.loader.jar.Handler. This Handler supports identifying more than one! /Delimiter and open correctlyURLConnection. The Connection opened is SpringBoot customorg.springframework.boot.loader.jar.JarURLConnectionThe implementation.
  2. Implemented ajava.net.JarURLConnectionA subclass oforg.springframework.boot.loader.jar.JarURLConnection. This link supports multiple! /Delimiter, and implements its own method to get InputStream in this case. And in order to be able toorg.springframework.boot.loader.jar.JarURLConnectionTo get the input stream right, SpringBoot has a custom set of utility classes and methods for reading a ZipFile. This part is closely related to the ZIP compression algorithm specification, so I won’t go into depth.

Able to read multiple! After the/url, things become very simple. Aforementioned ExecutableArchiveLauncher will launch method to the current FatJar build a JarFileArchive, and all through the object access to its internal resources URL, the URL contains a program compiled class and dependent jar package. SpringBoot custom handlers are passed in when these urls are built. Gets the URL of the array passed as a parameter to a custom ClassLoaderorg springframework.. The boot loader. LaunchedURLClassLoader. This ClassLoader inherits from UrlClassLoader. The UrlClassLoader attempts to load the class file from the resource that the Url points to. With custom handlers, it’s easy to try to retrieve resources from urls.

At this point, SpringBoot’s custom ClassLoader can load the class files of the dependent packages in FatJar.

extension

SpringBoot provides a good idea, but its internal implementation is quite complex, especially its own ZipFIle parser. But essentially all the work behind the scenes is to be able to read the Jar class file resources inside the FatJar. If there is a way to read these resources, you can actually load the Class file. JarFile, provided by the JDK itself, does just that. After reading all the resources, it is not too difficult to customize a ClassLoader to load the binary data and define the Class object. Of course, SpringBoot’s custom Zip resolution provides better performance by avoiding frequent file decompression during the class loading phase.


Lin Bin said Java, reprint please indicate the source, thank you.

Welcome to scan code attention