Springboot can be said to be a necessary skill for Java programmers. Everyone knows that Springboot can eventually be packaged into a jar package through maven, and then directly use the java -jar
command Run a web project (or other). This avoids the complicated operations of the original tomcat-based web project. Springboot can make the deployment of Web services so simple because it has a built-in Jetty (or Tomcat) server, and starts the server during the container startup process to successfully run the Web service.
I believe that all Springbooters will be familiar with the following code. Whether they are new students learning Springboot or new drivers starting to study Springboot source code, this code is almost our starting point. We are so familiar with it that we think it is the starting point of the magical paradise of Springboot. But is it really so?
@SpringBootApplication public class Springboot01helloworldApplication { public static void main(String[] args) { SpringApplication.run(Springboot01helloworldApplication.class, args); } }
We all know that after a Java project is packaged, the entry description of the jar package is written in the /META-INF/MANIFEST.MF
file. Let us take a look. Contents of this file:
Manifest-Version: 1.0 Archiver-Version: Plexus Archiver Built-By: MrXu Start-Class: com.vivo.internet.nex.repeater.console.RepeaterConsoleApplication Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Spring-Boot-Version: 1.5.19.RELEASE Created-By: Apache Maven 3.8.1 Build-Jdk: 1.8.0_281 Main-Class: org.springframework.boot.loader.JarLauncher
The description of the file entry is the value corresponding to Main-Class, which is org.springframework.boot.loader.JarLauncher
. So, next we need to take a look at what exactly this class does?
// JarLauncher.java 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() { } // ...省略无关代码 public static void main(String[] args) throws Exception { (new JarLauncher()).launch(args); } }
The obvious main function attracts our attention. Yes, this is the entrance. Look at the empty structure of JarLauncher and there is no code. Let’s first look for its parent class:
// ExecutableArchiveLauncher.java public abstract class ExecutableArchiveLauncher extends Launcher { public ExecutableArchiveLauncher() { try { this.archive = this.createArchive(); } catch (Exception var2) { throw new IllegalStateException(var2); } } // ...省略 } // Launcher.java public abstract class Launcher { public Launcher() {} // ...省略无关代码 }
As can be seen from the code, the parent class that actually does the work is ExecutableArchiveLauncher
, which constructs an archive instance during initialization, which encapsulates /META-INF/MANIFEST.MF
File information. We will also use it later.
Then comes the launch method, we only care about the core execution process:
// Launcher.java protected void launch(String[] args) throws Exception { JarFile.registerUrlProtocolHandler(); ClassLoader classLoader = this.createClassLoader(this.getClassPathArchives()); this.launch(args, this.getMainClass(), classLoader); } // ExecutableArchiveLauncher.java protected String getMainClass() throws Exception { Manifest manifest = this.archive.getManifest(); String mainClass = null; if (manifest != null) { mainClass = manifest.getMainAttributes().getValue("Start-Class"); } if (mainClass == null) { throw new IllegalStateException("No 'Start-Class' manifest entry specified in " + this); } else { return mainClass; } }
Here we first call the getMainClass method of the subclass ExecutableArchiveLauncher, the main logic is from /META-INF/MANIFEST. Get the Start-Class information from the MF
file, which corresponds to the com.vivo.internet.nex.repeater.console.RepeaterConsoleApplication
string, which is associated with the startup class we wrote.
Then comes the specific execution of the launch method. launch() first creates a MainMethodRunner, passes the Start-Class and transparent parameters obtained above, and then calls the run method of MainMethodRunner. The execution of the run method is also very simple, that is, loading the startup class corresponding to Start-Class, and then reflectively calling the main method of the startup class. After that comes the initialization process of the container.
// Launcher.java protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception { Thread.currentThread().setContextClassLoader(classLoader); // 这里首先调用createMainMethodRunner创建一个MainMethodRunner实例,将mainClass和args参数传入。随后调用 this.createMainMethodRunner(mainClass, args, classLoader).run(); } protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) { return new MainMethodRunner(mainClass, args); } // MainMethodRunner.java public MainMethodRunner(String mainClass, String[] args) { this.mainClassName = mainClass; this.args = args != null ? (String[])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((Object)null, this.args); }
The above is the detailed content of How to encapsulate the startup entry of SpringBoot application. For more information, please follow other related articles on the PHP Chinese website!