亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

spring boot jar的啟動原理解析

 更新時間:2018年02月05日 16:45:54   作者:葉長風(fēng)  
這篇文章主要介紹了 spring boot jar的啟動原理分析,非常不錯,具有參考借鑒價值,需要的朋友可以參考下

 1.前言

近來有空對公司的open api平臺進行了些優(yōu)化,然后在打出jar包的時候,突然想到以前都是對spring boot使用很熟練,但是從來都不知道spring boot打出的jar的啟動原理,然后這回將jar解開了看了下,與想象中確實大不一樣,以下就是對解壓出來的jar的完整分析。

2.jar的結(jié)構(gòu)

spring boot的應(yīng)用程序就不貼出來了,一個較簡單的demo打出的結(jié)構(gòu)都是類似,另外我采用的spring boot的版本為1.4.1.RELEASE網(wǎng)上有另外一篇文章對spring boot jar啟動的分析,那個應(yīng)該是1.4以下的,啟動方式與當(dāng)前版本也有著許多的不同。

在mvn clean install后,我們在查看target目錄中時,會發(fā)現(xiàn)兩個jar包,如下:

xxxx.jar
xxx.jar.original

這個則是歸功于spring boot插件的機制,將一個普通的jar打成了一個可以執(zhí)行的jar包,而xxx.jar.original則是maven打出的jar包,這些可以參考spring官網(wǎng)的文章來了解,如下:

http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#executable-jar

以下是spring boot應(yīng)用打出的jar的部分目錄結(jié)構(gòu),大部分省略了,僅僅展示出其中重要的部分。

.
├── 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

這個jar除了我們寫的應(yīng)用程序打出的class以外還有一個單獨的org包,應(yīng)該是spring boot應(yīng)用在打包的使用spring boot插件將這個package打進來,也就是增強了mvn生命周期中的package階段,而正是這個包在啟動過程中起到了關(guān)鍵的作用,另外中jar中將應(yīng)用所需的各種依賴都打進來,并且打入了spring boot額外的package,這種可以all-in-one的jar也被稱之為fat.jar,下文我們將一直以fat.jar來代替打出的jar的名字。

3.MANIFEST.MF文件

這個時候我們再繼續(xù)看META-INF中的MANIFEST.MF文件,如下:

Manifest-Version: 1.0
Implementation-Title: open :: server :: openapi
Implementation-Version: 1.0-SNAPSHOT
Archiver-Version: Plexus Archiver
Built-By: xiaxuan
Implementation-Vendor-Id: com.weibangong.open
Spring-Boot-Version: 1.4.1.RELEASE
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.PropertiesLauncher
Start-Class: com.weibangong.open.openapi.SpringBootWebApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.3.9
Build-Jdk: 1.8.0_20
Implementation-URL: http://maven.apache.org/open-server-openapi

這里指定的main-class是單獨打入的包中的一個類文件而不是我們的啟動程序,然后MANIFEST.MF文件有一個單獨的start-class指定的是我們的應(yīng)用的啟動程序。

4.啟動分析

首先我們找到類org.springframework.boot.loader.PropertiesLauncher,其中main方法為:

public static void main(String[] args) throws Exception {
  PropertiesLauncher launcher = new PropertiesLauncher();
  args = launcher.getArgs(args);
  launcher.launch(args);
}

查看launch方法,這個方法在父類Launcher中,找到父類方法launch方法,如下:

 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);
 }

   launch方法最終調(diào)用了createMainMethodRunner方法,后者實例化了MainMethodRunner對象并運行了run方法,我們轉(zhuǎn)到MainMethodRunner源碼中,如下:

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});
 }
}

查看run方法,就很怎么將spring boot的jar怎么運行起來的了,由此分析基本也就結(jié)束了。

5、main程序的啟動流程

講完了jar的啟動流程,現(xiàn)在來講下spring boot應(yīng)用中,main程序的啟動與加載流程,首先我們看一個spring boot應(yīng)用的main方法。

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
@EnableEurekaClient
public class A1ServiceApplication {
 public static void main(String[] args) {
  SpringApplication.run(A1ServiceApplication.class, args);
 }
}

轉(zhuǎn)到SpringApplication中的run方法,如下:

 /**
  * 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);
 }

這里的SpringApplication的實例化是關(guān)鍵,我們轉(zhuǎn)到SpringApplication的構(gòu)造函數(shù)。

 /**
  * 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();
 }

  這里的initialize方法中的deduceWebEnvironment()確定了當(dāng)前是以web應(yīng)用啟動還是以普通的jar啟動,如下:

 private boolean deduceWebEnvironment() {
  for (String className : WEB_ENVIRONMENT_CLASSES) {
   if (!ClassUtils.isPresent(className, null)) {
    return false;
   }
  }
  return true;
 }

其中的WEB_ENVIRONMENT_CLASSES為:  

 private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
   "org.springframework.web.context.ConfigurableWebApplicationContext" };

只要其中任何一個不存在,即當(dāng)前應(yīng)用以普通jar的形式啟動。

然后setInitializers方法初始化了所有的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))**

這一步初始化所有Listener。

我們再回到之前的SpringApplication(sources).run(args);處,進入run方法,代碼如下:

/**
  * 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);
  }
 }

這一步進行上下文的創(chuàng)建createAndRefreshContext(listeners, applicationArguments),

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());

這一步進行了環(huán)境的配置與加載。

  if (this.bannerMode != Banner.Mode.OFF) {
   printBanner(environment);
  }

這一步進行了打印spring boot logo,需要更改的話,在資源文件中加入banner.txt,banner.txt改為自己需要的圖案即可。

// Create, load, refresh and run the ApplicationContext
  context = createApplicationContext();
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass)

創(chuàng)建上下文,這一步中真正包含了是創(chuàng)建什么容器,并進行了響應(yīng)class的實例化,其中包括了EmbeddedServletContainerFactory的創(chuàng)建,是選擇jetty還是tomcat,內(nèi)容繁多,留待下一次再講。

if (this.registerShutdownHook) {
   try {
    context.registerShutdownHook();
   }
   catch (AccessControlException ex) {
    // Not allowed in some environments.
   }
  }

這一步就是當(dāng)前上下文進行注冊,當(dāng)收到kill指令的時候進行容器的銷毀等工作了。

基本到此,啟動的分析就結(jié)束了,但是還有一些細(xì)節(jié)講述起來十分耗時,這個留待后續(xù)的博文中再來講述,今天就到這里。

6.總結(jié)

綜上spring boot jar的啟動流程基本就是下面幾個步驟:

1、我們正常進行maven打包時,spring boot插件擴展maven生命周期,將spring boot相關(guān)package打入到j(luò)ar中,這個jar中包含了應(yīng)用所打出的jar以外還有spring boot啟動程序相關(guān)的類文件。

2、我以前看過稍微低一些版本的spring boot的jar的啟動流程,當(dāng)時我記得是當(dāng)前線程又起了一個新線程來運行main程序,而現(xiàn)在的已經(jīng)改成了直接使用反射來啟動main程序。

總結(jié)

以上所述是小編給大家介紹的spring boot jar的啟動原理解析,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Java中List集合按指定條件排序

    Java中List集合按指定條件排序

    這篇文章主要介紹了Java中List集合按指定條件排序,List是一種有序集合,可以隨時添加和刪除其中元素,本篇文章針對List集合按照條件排序的幾種方式做了實例演示,需要的朋友可以參考下
    2023-07-07
  • 詳解idea搭建springboot+mybatis框架的教程

    詳解idea搭建springboot+mybatis框架的教程

    這篇文章主要介紹了詳解idea搭建springboot+mybatis框架的教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • JAVA生產(chǎn)者消費者(線程同步)代碼學(xué)習(xí)示例

    JAVA生產(chǎn)者消費者(線程同步)代碼學(xué)習(xí)示例

    這篇文章主要介紹了JAVA線程同步的代碼學(xué)習(xí)示例,大家參考使用吧
    2013-11-11
  • 解決線程并發(fā)redisson使用遇到的坑

    解決線程并發(fā)redisson使用遇到的坑

    這篇文章主要介紹了解決線程并發(fā)redisson使用遇到的坑,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • springboot如何去除debug日志

    springboot如何去除debug日志

    這篇文章主要介紹了springboot如何去除debug日志的方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • IDEA新建Springboot項目(圖文教程)

    IDEA新建Springboot項目(圖文教程)

    下面小編就為大家?guī)硪黄狪DEA新建Springboot項目(圖文教程)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • java冒泡排序和快速排序代碼

    java冒泡排序和快速排序代碼

    本文主要介紹了java冒泡排序和快速排序的實例代碼。具有很好的參考價值。下面跟著小編一起來看下吧
    2017-04-04
  • SpringBoot利用自定義注解實現(xiàn)多數(shù)據(jù)源

    SpringBoot利用自定義注解實現(xiàn)多數(shù)據(jù)源

    這篇文章主要為大家詳細(xì)介紹了SpringBoot如何利用自定義注解實現(xiàn)多數(shù)據(jù)源效果,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,需要的可以了解一下
    2022-10-10
  • springboot如何整合elasticsearch

    springboot如何整合elasticsearch

    這篇文章主要介紹了springboot如何整合elasticsearch問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • SpringBoot3整合MyBatis出現(xiàn)異常:Property?'sqlSessionFactory'or?'sqlSessionTemplate'?are?required

    SpringBoot3整合MyBatis出現(xiàn)異常:Property?'sqlSessionFactory&a

    這篇文章主要介紹了SpringBoot3整合MyBatis報錯:Property?‘sqlSessionFactory‘?or?‘sqlSessionTemplate‘?are?required,其實不是個大問題,只是自己編碼時遇到了,然后總結(jié)總結(jié)分享一下,如果有遇到類似問題的,可以參考一下
    2022-11-11

最新評論