Spring boot jar startup Principle Analysis, springjar
1. Preface
Recently, I have made some optimizations to the company's open api platform. When I typed a jar package, I suddenly thought that I used spring boot very well, however, I never knew how to start the jar program in spring boot. After that, I unwrapped the jar program and read it. It is indeed different from what I imagined, the following is a complete analysis of the extracted jar.
2. jar Structure
The spring boot application will not be pasted out. The structure of a simple demo is similar, in addition, the spring boot version I used is 1.4.1.RELEASE. There is another article on the Internet to analyze the startup of spring boot jar, which should be less than 1.4, the startup method is different from the current version.
After mvn clean install, we will find two jar packages in the target directory, as shown below:
xxxx.jarxxx.jar.original
This is due to the mechanism of the spring boot plug-in. A common jar is made into an executable jar package, and xxx. jar. original is the jar package produced by maven. You can refer to the articles on the spring official website for details as follows:
Http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#executable-jar
The following is the directory structure of some jar files produced by the spring boot application. Most of the directories are omitted and only important parts are shown.
.├── BOOT-INF│ ├── classes│ │ ├── application-dev.properties│ │ ├── application-prod.properties│ │ ├── application.properties│ │ ├── com│ │ │ └── weibangong│ │ │ └── open│ │ │ └── openapi│ │ │ ├── SpringBootWebApplication.class│ │ │ ├── config│ │ │ │ ├── ProxyServletConfiguration.class│ │ │ │ └── SwaggerConfig.class│ │ │ ├── oauth2│ │ │ │ ├── controller│ │ │ │ │ ├── AccessTokenController.class│ │ ├── logback-spring.xml│ │ └── static│ │ ├── css│ │ │ └── guru.css│ │ ├── images│ │ │ ├── FBcover1200x628.png│ │ │ └── NewBannerBOOTS_2.png│ └── lib│ ├── accessors-smart-1.1.jar├── META-INF│ ├── MANIFEST.MF│ └── maven│ └── com.weibangong.open│ └── open-server-openapi│ ├── pom.properties│ └── pom.xml└── org └── springframework └── boot └── loader ├── ExecutableArchiveLauncher$1.class ├── ExecutableArchiveLauncher.class ├── JarLauncher.class ├── LaunchedURLClassLoader$1.class ├── LaunchedURLClassLoader.class ├── Launcher.class ├── archive │ ├── Archive$Entry.class │ ├── Archive$EntryFilter.class │ ├── Archive.class │ ├── ExplodedArchive$1.class │ ├── ExplodedArchive$FileEntry.class │ ├── ExplodedArchive$FileEntryIterator$EntryComparator.class ├── ExplodedArchive$FileEntryIterator.class
In addition to the class of the application we wrote, this jar also has a separate org package, which should be packaged by the spring boot application and packaged with the spring boot plug-in, that is to say, the package stage in the mvn lifecycle is enhanced, and this package plays a key role in the startup process. In addition, all the dependencies required by the application are included in the jar, and the extra package of spring boot is entered. The jar which can be all-in-one is also called fat. jar. jar to replace the name of the jar.
3. MANIFEST. MF File
At this time we continue to look at the MANIFEST. MF file in the META-INF, as follows:
Manifest-Version: 1.0Implementation-Title: open :: server :: openapiImplementation-Version: 1.0-SNAPSHOTArchiver-Version: Plexus ArchiverBuilt-By: xiaxuanImplementation-Vendor-Id: com.weibangong.openSpring-Boot-Version: 1.4.1.RELEASEImplementation-Vendor: Pivotal Software, Inc.Main-Class: org.springframework.boot.loader.PropertiesLauncherStart-Class: com.weibangong.open.openapi.SpringBootWebApplicationSpring-Boot-Classes: BOOT-INF/classes/Spring-Boot-Lib: BOOT-INF/lib/Created-By: Apache Maven 3.3.9Build-Jdk: 1.8.0_20Implementation-URL: http://maven.apache.org/open-server-openapi
The specified main-class is a class file in the package separately, instead of our startup program, and then MANIFEST. the MF file has a separate start-class that specifies the Startup Program of our application.
4. Start Analysis
First, find the org. springframework. boot. loader. PropertiesLauncher class, where the main method is:
public static void main(String[] args) throws Exception { PropertiesLauncher launcher = new PropertiesLauncher(); args = launcher.getArgs(args); launcher.launch(args);}
Check the launch method. In the parent class Launcher, find the parent class method launch as follows:
protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception { Thread.currentThread().setContextClassLoader(classLoader); this.createMainMethodRunner(mainClass, args, classLoader).run(); } protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) { return new MainMethodRunner(mainClass, args); }
The launch method finally calls the createMainMethodRunner method. The latter instantiates the MainMethodRunner object and runs the run method. We go to the MainMethodRunner source code, as shown below:
package org.springframework.boot.loader;import java.lang.reflect.Method;public class MainMethodRunner { private final String mainClassName; private final String[] args; public MainMethodRunner(String mainClass, String[] args) { this.mainClassName = mainClass; this.args = args == null?null:(String[])args.clone(); } public void run() throws Exception { Class mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName); Method mainMethod = mainClass.getDeclaredMethod("main", new Class[]{String[].class}); mainMethod.invoke((Object)null, new Object[]{this.args}); }}
Check the run method to see how to run the jar of spring boot, and the analysis is complete.
5. main Program Startup Process
After completing the jar startup process, let's take a look at the main method of the spring boot application.
package cn.com.devh;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.cloud.netflix.feign.EnableFeignClients;/** * Created by xiaxuan on 17/8/25. */@SpringBootApplication@EnableFeignClients@EnableEurekaClientpublic class A1ServiceApplication { public static void main(String[] args) { SpringApplication.run(A1ServiceApplication.class, args); }}
Go to the run method in spring application as follows:
/** * Static helper that can be used to run a {@link SpringApplication} from the * specified source using default settings. * @param source the source to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[] { source }, args); } /** * Static helper that can be used to run a {@link SpringApplication} from the * specified sources using default settings and user supplied arguments. * @param sources the sources to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); }
The instantiation of SpringApplication is the key here. We will go to the SpringApplication constructor.
/** * Create a new {@link SpringApplication} instance. The application context will load * beans from the specified sources (see {@link SpringApplication class-level} * documentation for details. The instance can be customized before calling * {@link #run(String...)}. * @param sources the bean sources * @see #run(Object, String[]) * @see #SpringApplication(ResourceLoader, Object...) */ public SpringApplication(Object... sources) { initialize(sources); } private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } this.webEnvironment = deduceWebEnvironment(); setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
In the initialize method, deduceWebEnvironment () determines whether to start a web application or a common jar, as follows:
private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return false; } } return true; }
The WEB_ENVIRONMENT_CLASSES is:
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" };
If none of them exists, the current application is started as a normal jar.
Then, the setInitializers method initializes all ApplicationContextInitializer,
/** * Sets the {@link ApplicationContextInitializer} that will be applied to the Spring * {@link ApplicationContext}. * @param initializers the initializers to set */ public void setInitializers( Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<ApplicationContextInitializer<?>>(); this.initializers.addAll(initializers); }setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class))**
Initialize all Listener in this step.
Go back to SpringApplication (sources). run (args); and enter the run method. The Code is as follows:
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.started(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); context = createAndRefreshContext(listeners, applicationArguments); afterRefresh(context, applicationArguments); listeners.finished(context, null); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } return context; } catch (Throwable ex) { handleRunFailure(context, listeners, ex); throw new IllegalStateException(ex); } }
Create createAndRefreshContext (listeners, applicationArguments) in this step ),
private ConfigurableApplicationContext createAndRefreshContext( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { ConfigurableApplicationContext context; // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment); if (isWebEnvironment(environment) && !this.webEnvironment) { environment = convertToStandardEnvironment(environment); } if (this.bannerMode != Banner.Mode.OFF) { printBanner(environment); } // Create, load, refresh and run the ApplicationContext context = createApplicationContext(); context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); // Load the sources Set<Object> sources = getSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[sources.size()])); listeners.contextLoaded(context); // Refresh the context refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } return context; }// Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs());
This step configures and loads the environment.
if (this.bannerMode != Banner.Mode.OFF) { printBanner(environment); }
In this step, you can print the spring boot logo, modify it, add banner.txt to the resource file, and change banner.txt to the desired pattern.
// Create, load, refresh and run the ApplicationContext context = createApplicationContext();return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass)
Create a context. This step actually contains the container to be created and the response class is instantiated. It includes the creation of EmbeddedServletContainerFactory, whether to select jetty or tomcat. There are many contents, wait for the next lecture.
if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } }
This step is to register the current context and destroy the container when receiving the kill command.
Basically, the start analysis is over, but there are still some details that are time-consuming. This will be discussed later in the blog post. It will be here today.
6. Summary
To sum up, the startup process of spring boot jar is basically the following steps:
1. During Normal maven packaging, the spring boot plug-in expands the maven life cycle and inserts the package related to spring boot into the jar, this jar contains other jar files related to the spring boot Startup Program.
2. I have seen the Startup Process of a slightly lower version of spring boot jar. At that time, I remember that the current thread started a new thread to run the main program, now we have changed to Using Reflection directly to start the main program.
Summary
The above is the startup Principle Analysis of spring boot jar introduced by xiaobian. I hope it will be helpful to you. If you have any questions, please leave a message and I will reply to you in a timely manner. Thank you very much for your support for the help House website!