小编给大家分享一下spring boot jar启动原理的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

创新互联服务项目包括广南网站建设、广南网站制作、广南网页制作以及广南网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,广南网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到广南省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
1.前言
近来对公司的open api平台进行了些优化,然后在打出jar包的时候,突然想到以前都是对spring boot使用很熟练,但是从来都不知道spring boot打出的jar的启动原理,然后这回将jar解开了看了下,与想象中确实大不一样,以下就是对解压出来的jar的完整分析。
2.jar的结构
spring boot的应用程序就不贴出来了,一个较简单的demo打出的结构都是类似,另外我采用的spring boot的版本为1.4.1.RELEASE网上有另外一篇文章对spring boot jar启动的分析,那个应该是1.4以下的,启动方式与当前版本也有着许多的不同。
在mvn clean install后,我们在查看target目录中时,会发现两个jar包,如下:
xxxx.jar xxx.jar.original
这个则是归功于spring boot插件的机制,将一个普通的jar打成了一个可以执行的jar包,而xxx.jar.original则是maven打出的jar包,这些可以参考spring官网的文章来了解,如下:
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#executable-jar
以下是spring boot应用打出的jar的部分目录结构,大部分省略了,仅仅展示出其中重要的部分。
. ├── BOOT-INF │ ├── classes │ │ ├── application-dev.properties │ │ ├── application-prod.properties │ │ ├── application.properties │ │ ├── com │ │ │ └── weibangong │ │ │ └── open │ │ │ └── openapi │ │ │ ├── SpringBootWebApplication.class │ │ │ ├── config │ │ │ │ ├── ProxyServletConfiguration.class │ │ │ │ └── SwaggerConfig.class │ │ │ ├── oauth3 │ │ │ │ ├── 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除了我们写的应用程序打出的class以外还有一个单独的org包,应该是spring boot应用在打包的使用spring boot插件将这个package打进来,也就是增强了mvn生命周期中的package阶段,而正是这个包在启动过程中起到了关键的作用,另外中jar中将应用所需的各种依赖都打进来,并且打入了spring boot额外的package,这种可以all-in-one的jar也被称之为fat.jar,下文我们将一直以fat.jar来代替打出的jar的名字。
3.MANIFEST.MF文件
这个时候我们再继续看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指定的是我们的应用的启动程序。
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方法最终调用了createMainMethodRunner方法,后者实例化了MainMethodRunner对象并运行了run方法,我们转到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怎么运行起来的了,由此分析基本也就结束了。
5、main程序的启动流程
讲完了jar的启动流程,现在来讲下spring boot应用中,main程序的启动与加载流程,首先我们看一个spring boot应用的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);
 }
}转到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的实例化是关键,我们转到SpringApplication的构造函数。
 /**
  * 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()确定了当前是以web应用启动还是以普通的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" };只要其中任何一个不存在,即当前应用以普通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>();
  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);
  }
 }这一步进行上下文的创建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这一步进行了环境的配置与加载。
  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)
创建上下文,这一步中真正包含了是创建什么容器,并进行了响应class的实例化,其中包括了EmbeddedServletContainerFactory的创建,是选择jetty还是tomcat,内容繁多,留待下一次再讲。
if (this.registerShutdownHook) {
   try {
    context.registerShutdownHook();
   }
   catch (AccessControlException ex) {
    // Not allowed in some environments.
   }
  }这一步就是当前上下文进行注册,当收到kill指令的时候进行容器的销毁等工作了。
以上是“spring boot jar启动原理的示例分析”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联行业资讯频道!
本文题目:springbootjar启动原理的示例分析
当前网址:http://www.cqwzjz.cn/article/poodje.html

 建站
建站
 咨询
咨询 售后
售后
 建站咨询
建站咨询 
 