Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Why SpringBoot can be started with Jar package

2025-01-19 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article will explain in detail why SpringBoot can be started with the Jar package. The editor thinks it is very practical, so I share it for you as a reference. I hope you can get something after reading this article.

Introduction

Many beginners will be confused about how Spring Boot packages the application code and all the dependencies into a separate Jar package, because after traditional Java projects are packaged into Jar packages, they need to specify dependencies through the-classpath attribute before they can run.

Spring Boot packaging plug-in

Spring Boot provides a maven project packaging plug-in called spring-boot-maven-plugin, as follows:

Org.springframework.boot spring-boot-maven-plugin

It is convenient to package Spring Boot projects into jar packages. This eliminates the need to deploy Web server containers such as Tomcat, Jetty, and so on.

Let's take a look at the packaged structure of Spring Boot. When we open the target directory, we find that there are two jar packages:

Among them, springboot-0.0.1-SNAPSHOT.jar is a packaging plug-in provided by Spring Boot and typed into Fat Jar in a new format, including all the dependencies

Springboot-0.0.1-SNAPSHOT.jar.original, on the other hand, is generated by native packaging of Java and contains only the content of the project itself.

The organizational structure of SpringBoot FatJar

The structure of the executable Jar expanded by Spring Boot is as follows:

BOOT-INF directory: contains our project code (classes directory) and required dependencies (lib directory)

META-INF directory: provides the metadata of the Jar package through the MANIFEST.MF file, declaring the startup class of jar

Org.springframework.boot.loader: Spring Boot loader code that implements the magic source of Jar in Jar loading.

We see that if you remove the BOOT-INF directory, this will be a very common and standard Jar package, including meta-information and executable code parts, whose / META-INF/MAINFEST.MF specifies the startup meta-information of the Jar package, and org.springframework.boot.loader performs the corresponding logical operation.

MAINFEST.MF meta-information

The meta-information content is as follows:

Manifest-Version: 1.0Spring-Boot-Classpath-Index: BOOT-INF/classpath.idxImplementation-Title: springbootImplementation-Version: 0.0.1-SNAPSHOTSpring-Boot-Layers-Index: BOOT-INF/layers.idxStart-Class: com.listenvision.SpringbootApplicationSpring-Boot-Classes: BOOT-INF/classes/Spring-Boot-Lib: BOOT-INF/lib/Build-Jdk-Spec: 1.8Spring-Boot-Version: 2.5.6Created-By: Maven Jar Plugin 3.2.0Main-Class: org.springframework.boot.loader.JarLauncher

It is equivalent to a Properties configuration file, and each line is a configuration item. Let's focus on two configuration items:

Main-Class configuration item: the startup class of the jar package specified by Java, which is set to the JarLauncher class of the spring-boot-loader project to start the Spring Boot application.

Start-Class configuration item: the main startup class specified by Spring Boot, which is set to the Application class we defined.

Spring-Boot-Classes configuration item: specifies the entry to load the application class.

Spring-Boot-Lib configuration item: specifies the library on which the application is loaded.

Startup principle

The startup principle of Spring Boot is shown in the following figure:

Source code analysis JarLauncher

The JarLauncher class is the startup class for the Spring Boot jar package, and the complete class diagram is shown below:

The WarLauncher class is the startup class for the Spring Boot war package. The startup class org.springframework.boot.loader.JarLauncher is not introduced into the project, but is appended by the spring-boot-maven-plugin plug-in repackage.

Next, let's take a look at the source code of JarLauncher, which is relatively simple, as shown in the following figure:

Public class JarLauncher extends ExecutableArchiveLauncher {private static final String DEFAULT_CLASSPATH_INDEX_LOCATION = "BOOT-INF/classpath.idx"; static final EntryFilter NESTED_ARCHIVE_ENTRY_FILTER = (entry)-> {if (entry.isDirectory ()) {return entry.getName () .equals ("BOOT-INF/classes/");} return entry.getName () .startsWith ("BOOT-INF/lib/");} Public JarLauncher () {} protected JarLauncher (Archive archive) {super (archive);} @ Override protected ClassPathIndexFile getClassPathIndex (Archive archive) throws IOException {/ / Only needed for exploded archives, regular ones already have a defined order if (archive instanceof ExplodedArchive) {String location = getClassPathIndexFileLocation (archive); return ClassPathIndexFile.loadIfPossible (archive.getUrl (), location) } return super.getClassPathIndex (archive);} private String getClassPathIndexFileLocation (Archive archive) throws IOException {Manifest manifest = archive.getManifest (); Attributes attributes = (manifest! = null)? Manifest.getMainAttributes (): null; String location = (attributes! = null)? Attributes.getValue (BOOT_CLASSPATH_INDEX_ATTRIBUTE): null; return (location! = null)? Location: DEFAULT_CLASSPATH_INDEX_LOCATION;} @ Override protected boolean isPostProcessingClassPathArchives () {return false;} @ Override protected boolean isSearchCandidate (Archive.Entry entry) {return entry.getName () .startsWith ("BOOT-INF/");} @ Override protected boolean isNestedArchive (Archive.Entry entry) {return NESTED_ARCHIVE_ENTRY_FILTER.matches (entry) } public static void main (String [] args) throws Exception {/ / call the launch method new JarLauncher () .launch (args) defined by the base class Launcher;}}

It mainly looks at its main method, which calls the launch method defined by the base class Launcher, and Launcher is the parent class of ExecutableArchiveLauncher. Let's take a look at the Launcher base class source code:

Launcherpublic abstract class Launcher {private static final String JAR_MODE_LAUNCHER = "org.springframework.boot.loader.jarmode.JarModeLauncher"; protected void launch (String [] args) throws Exception {if (! isExploded ()) {JarFile.registerUrlProtocolHandler ();} ClassLoader classLoader = createClassLoader (getClassPathArchivesIterator ()); String jarMode = System.getProperty ("jarmode"); String launchClass = (jarMode! = null & &! jarMode.isEmpty ()? JAR_MODE_LAUNCHER: getMainClass (); launch (args, launchClass, classLoader);} @ Deprecated protected ClassLoader createClassLoader (List archives) throws Exception {return createClassLoader (archives.iterator ());} protected ClassLoader createClassLoader (Iterator archives) throws Exception {List urls = new ArrayList (50); while (archives.hasNext ()) {urls.add (archives.next (). GetUrl ()) } return createClassLoader (urls.toArray (new URL [0]));} protected ClassLoader createClassLoader (URL [] urls) throws Exception {return new LaunchedURLClassLoader (isExploded (), getArchive (), urls, getClass (). GetClassLoader ());} protected void launch (String [] args, String launchClass, ClassLoader classLoader) throws Exception {Thread.currentThread (). SetContextClassLoader (classLoader); createMainMethodRunner (launchClass, args, classLoader). Run () } protected MainMethodRunner createMainMethodRunner (String mainClass, String [] args, ClassLoader classLoader) {return new MainMethodRunner (mainClass, args);} protected abstract String getMainClass () throws Exception; protected Iterator getClassPathArchivesIterator () throws Exception {return getClassPathArchives () .iterator ();} @ Deprecated protected List getClassPathArchives () throws Exception {throw new IllegalStateException ("Unexpected call to getClassPathArchives ()") } protected final Archive createArchive () throws Exception {ProtectionDomain protectionDomain = getClass () .getProtectionDomain (); CodeSource codeSource = protectionDomain.getCodeSource (); URI location = (codeSource! = null)? CodeSource.getLocation () .toURI (): null; 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);} return (root.isDirectory ()? New ExplodedArchive (root): new JarFileArchive (root);} protected boolean isExploded () {return false;} protected Archive getArchive () {return null;}}

The launch method first creates a classloader and then determines whether jar sets the jarmode property in the MANIFEST.MF file.

If it is not set, the value of launchClass is returned from getMainClass (), which is implemented by the PropertiesLauncher subclass and returns the value of the Start-Class property configured in MANIFEST.MF.

Call the createMainMethodRunner method, build a MainMethodRunner object, and call its run method.

PropertiesLauncher@Overrideprotected String getMainClass () throws Exception {/ / load the Start-Class configuration in the MANIFEST.MF file under the jar package target directory, and find the startup class String mainClass = getProperty (MAIN, "Start-Class") of springboot; if (mainClass = = null) {throw new IllegalStateException ("No'" + MAIN + "'or 'Start-Class' specified");} return mainClass;} MainMethodRunner

For the executor of the target class main method, the mainClassName is assigned to the Start-Class attribute value configured in MANIFEST.MF, that is, com.listenvision.SpringbootApplication, and then the main method of SpringbootApplication is executed through reflection, thus achieving the effect of starting Spring Boot.

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 = Class.forName (this.mainClassName, false, Thread.currentThread (). GetContextClassLoader ()); Method mainMethod = mainClass.getDeclaredMethod ("main", String [] .class); mainMethod.setAccessible (true); mainMethod.invoke (null, new Object [] {this.args})) }} this is the end of the article on "Why SpringBoot can be started with Jar package". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report