编程知识 cdmana.com

Why can springboot jar run independently

Welcome to visit mine GitHub

https://github.com/zq2599/blog_demos

Content : All original articles classified summary and supporting source code , involve Java、Docker、Kubernetes、DevOPS etc. ;

Welcome to visit mine GitHub

https://github.com/zq2599/blog_demos Content : All original articles classified summary and supporting source code , involve Java、Docker、Kubernetes、DevOPS etc. ;

Capable of operating independently jar file

Developing springboot When applied , adopt <font color="blue">java -jar</font> Command to start an application is a common way , Today, let's learn about the technology behind this simple operation ;

Development demo

To develop a springboot Application as the object of this study , The corresponding version information is as follows :

  • JDK:1.8.0_211
  • springboot:2.3.1.RELEASE
  • maven:3.6.0

Next, develop springboot application , This application is very simple :

  1. springboot Application name is <font color="blue">springbootstarterdemo</font>,pom.xml The contents of the document :
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://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.3.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.bolingcavalry</groupId>
    <artifactId>springbootstarterdemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springbootstarterdemo</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>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
  1. only one java class , There's a http Interface :
package com.bolingcavalry.springbootstarterdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

@SpringBootApplication
@RestController
public class SpringbootstarterdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootstarterdemoApplication.class, args);
    }
    @RequestMapping(value = "/hello")
    public String hello(){
        return "hello " + new Date();
    }
}
  1. Coding complete , stay pom.xml The directory where the command is executed
mvn clean package -U -DskipTests
  1. After a successful build , stay target Directory to get the file <font color="blue">springbootstarterdemo-0.0.1-SNAPSHOT.jar</font>
  2. This is this. <font color="blue">springbootstarterdemo-0.0.1-SNAPSHOT.jar</font>, Execute at this time <font color="blue">java -jar springbootstarterdemo-0.0.1-SNAPSHOT.jar</font> You can start the app , Here's the picture :  Insert picture description here Next use this <font color="blue">springbootstarterdemo-0.0.1-SNAPSHOT.jar</font> To analyze jar Why files can be started independently ;

java -jar What did you do

  • First, make it clear <font color="blue">java -jar</font> What did the order do , stay oracle Official website Found the description of the command :

<font color="blue"> 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. </font>

  • Show me my poor English translation again :
  1. Use <font color="blue">-jar</font> When parameters are , The following parameter is jar file name ( In this case, it is springbootstarterdemo-0.0.1-SNAPSHOT.jar);
  2. The jar The file contains class And resource files ;
  3. stay manifest In file <font color="blue">Main-Class</font> The definition of ;
  4. <font color="blue">Main-Class</font> The source code of the entire application specifies the startup class ;(in its source code)

To sum up : <font color="blue">java -jar</font> Will go to jar Medium manifest file , Find the real startup class in there ;

exploration springbootstarterdemo-0.0.1-SNAPSHOT.jar

  1. <font color="blue">springbootstarterdemo-0.0.1-SNAPSHOT.jar</font> It's in front. springboot The result of the construction of the project , It's a compressed package , You can decompress with common compression tools , My environment here is MacBook Pro, use unzip To extract the ;
  2. There is a lot of content after decompressing , Let's focus on manifest dependent , In the red box below is manifest file :  Insert picture description here
  3. Open the file in the red box above , The contents are as follows :
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Implementation-Title: springbootstarterdemo
Implementation-Version: 0.0.1-SNAPSHOT
Start-Class: com.bolingcavalry.springbootstarterdemo.Springbootstarter
 demoApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.3.1.RELEASE
Created-By: Maven Jar Plugin 3.2.0
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.JarLauncher
  1. It can be seen from the above Main-Class Value <font color="blue">org.springframework.boot.loader.JarLauncher</font>, This and the front java Official documents correspond to , It's this JarLauncher The actual startup class is specified in the code of the class ;

Doubts arise

  1. stay MANIFEST.MF There's a line in the file :
Start-Class: com.bolingcavalry.springbootstarterdemo.Springbootstarter
 demoApplication
  1. Ahead java In official documents , Only mentioned <font color="blue">Main-Class </font>, Not mentioned <font color="blue">Start-Class</font>;
  2. Start-Class The value of is SpringbootstarterdemoApplication, This is ours java The only class in the code , Only the real application startup class ;
  3. So the question is : In theory , perform java -jar On command JarLauncher Class will be executed , But it's actually SpringbootstarterdemoApplication Carried out , What happened to this ?

guess

Guess before you do it , I think the reasons should be as follows :

  1. java -jar Command will start JarLauncher;
  2. <font color="blue">Start-Class</font> It's for JarLauncher With ;
  3. JarLauncher according to <font color="blue">Start-Class</font> eureka SpringbootstarterdemoApplication, And then execute it ;

analysis JarLauncher

  1. To download SpringBoot Source code , I downloaded it 2.3.1 edition , Address :https://github.com/spring-projects/spring-boot/releases/tag/v2.3.1.RELEASE
  2. JarLauncher The project is spring-boot-loader, Find out first JarLauncher Inheritance relationship of , Here's the picture , so JarLauncher Inherited from ExecutableArchiveLauncher, and ExecutableArchiveLauncher Parent class of Launcher At the top , It's an abstract class :

 Insert picture description here 3. java -jar Execution is JarLauncher Of main Method , as follows , Will instantiate a JarLauncher object , Then execute it launch Method , And bring all the input parameters into :

public static void main(String[] args) throws Exception {
	new JarLauncher().launch(args);
}
  1. above launch Method in the parent class Launcher in :
protected void launch(String[] args) throws Exception {
    //  take jar The way to run after decompression is called exploded mode
    //  If it is exploded mode, Can't support the passage of URL load jar
    //  If not exploded mode, You can go through URL load jar
	if (!isExploded()) {
	    //  If allowed to pass URL load jar, Register the corresponding processing class here 
		JarFile.registerUrlProtocolHandler();
	}
	//  establish classLoader
	ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
	// jarmode Is to create docker The parameters used in the mirror image , This parameter is used to generate a file with multiple layer Mirror image of information 
	//  I don't care about jarmode
	String jarMode = System.getProperty("jarmode");
	// without jarmode Parameters ,launchClass The value of comes from getMainClass() return 
	String launchClass = (jarMode != null && !jarMode.isEmpty()) ? JAR_MODE_LAUNCHER : getMainClass();
	launch(args, launchClass, classLoader);
}
  1. It can be seen that we should focus on <font color="blue">getMainClass()</font> Method , Before we look at this method , Let's first focus on an important member variable <font color="blue">archive</font>, yes JarLauncher Parent class of ExecutableArchiveLauncher Of archive, As can be seen below , This variable comes from the method <font color="blue">createArchive</font>:
public ExecutableArchiveLauncher() {
		try {
			this.archive = createArchive();
			this.classPathIndex = getClassPathIndex(this.archive);
		}
		catch (Exception ex) {
			throw new IllegalStateException(ex);
		}
	}
  1. Method comes from Launcher.createArchive, As shown below , Visible member variables <font color="blue">archive</font> It's actually a JarFileArchive object :
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));
	}
  1. Now back to getMainClass() Method , so <font color="blue">this.archive.getManifest</font> Method returns <font color="red">META-INF/MANIFEST.MF</font> The content of the document , then getValue(START_CLASS_ATTRIBUTE) The method is actually from META-INF/MANIFEST.MF We have achieved Start-Class Properties of :
@Override
	protected String getMainClass() throws Exception {
	    //  The corresponding is JarFileArchive.getManifest Method ,
	    //  When I went in, I found that the corresponding one was JarFile.getManifest Method ,
	    // JarFile.getManifest The corresponding is META-INF/MANIFEST.MF The content of the document 
		Manifest manifest = this.archive.getManifest();
		String mainClass = null;
		if (manifest != null) {
		    //  The corresponding is META-INF/MANIFEST.MF In the document Start-Class Properties of 
			mainClass = manifest.getMainAttributes().getValue(START_CLASS_ATTRIBUTE);
		}
		if (mainClass == null) {
			throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this);
		}
		return mainClass;
	}
  1. From the above analysis, we can see that :getMainClass() Method returns META-INF/MANIFEST.MF We have achieved <font color="red">Start-Class</font> Properties of com.bolingcavalry.springbootstarterdemo.SpringbootstarterdemoApplication, Back again launch In the method , You can see that the final running code is launch(args, launchClass, classLoader), its launchClass The parameter is com.bolingcavalry.springbootstarterdemo.SpringbootstarterdemoApplication:
protected void launch(String[] args) throws Exception {
		if (!isExploded()) {
			JarFile.registerUrlProtocolHandler();
		}
		ClassLoader classLoader = createClassLoader(getClassPathArchivesIterator());
		String jarMode = System.getProperty("jarmode");
		//  there launchClass be equal to "com.bolingcavalry.springbootstarterdemo.SpringbootstarterdemoApplication"
		String launchClass = (jarMode != null && !jarMode.isEmpty()) ? JAR_MODE_LAUNCHER : getMainClass();
		//  This is the start SpringbootstarterdemoApplication The place of 
		launch(args, launchClass, classLoader);
	}
  1. an launch(args, launchClass, classLoader), In the end MainMethodRunner class :
public class MainMethodRunner {

	private final String mainClassName;

	private final String[] args;

	/**
	 * Create a new {@link MainMethodRunner} instance.
	 * @param mainClass the main class
	 * @param args incoming arguments
	 */
	public MainMethodRunner(String mainClass, String[] args) {
	    // mainClassName To be an assignment "com.bolingcavalry.springbootstarterdemo.SpringbootstarterdemoApplication"
		this.mainClassName = mainClass;
		this.args = (args != null) ? args.clone() : null;
	}

	public void run() throws Exception {
	    //  obtain SpringbootstarterdemoApplication Of Class object 
		Class<?> mainClass = Class.forName(this.mainClassName, false, Thread.currentThread().getContextClassLoader());
		//  obtain SpringbootstarterdemoApplication Of main Method object 
		Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
		mainMethod.setAccessible(true);
		//  By reflection main Method 
		mainMethod.invoke(null, new Object[] { this.args });
	}
}

finally , The truth is clear ;

Summary

Finally, make a summary as short as possible , First look at jar How did it come about , Here's the picture ,maven Plug in generated jar In file , There are common class、jar, There's something in line with java canonical MANIFEST.MF file , also , still MANIFEST.MF An additional file named <font color="blue">Start-Class</font> Configuration of , Here is the application startup class we wrote <font color="blue">SpringbootstarterdemoApplication</font>:

 Insert picture description here

The startup class is <font color="blue">JarLauncher</font>, How does it relate to MANIFEST.MF What about the files ? As can be seen from the figure below , And finally through JarFile Member variables of class <font color="blue">manifestSupplier</font> Related :

 Insert picture description here Let's look at the execution of the key code , Here's the picture :

 Insert picture description here

thus ,SpringBoot Of jar The basic principle of independent operation is clear , In the process of inquiry , Besides being familiar with the key code flow , Also on the jar More about the documents in , If you are learning SpringBoot, I hope this article can give you some reference ;

Official documents

  1. The last attached SpringBoot Official documents , You can see <font color="blue">Start-Class</font> Description information :

 Insert picture description here

  1. The above document clearly mentions :Start-Class It defines the actual startup class , You should know everything at this time , The feeling that this should have happened ;

You are not alone , Xinchen's original works are accompanied all the way

  1. Java series
  2. Spring series
  3. Docker series
  4. kubernetes series
  5. database + Middleware family
  6. DevOps series

Welcome to the official account : Xinchen, programmer

WeChat search 「 Xinchen, programmer 」, I'm Xinchen , Looking forward to traveling with you Java The world ... https://github.com/zq2599/blog_demos

版权声明
本文为[Programmer Xinchen]所创,转载请带上原文链接,感谢
https://cdmana.com/2020/12/20201225102952153u.html

Scroll to Top