• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >Java > Spring Boot启动过程完全解析(一)

Spring Boot启动过程完全解析(一)

作者:draculav 字体:[增加 减小] 来源:互联网 时间:2017-05-28

draculav 通过本文主要向大家介绍了spring boot 启动,spring boot启动报错,spring boot 启动失败,spring boot怎么启动,spring boot 启动方式等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE。

  首先,普通的入口,这没什么好说的,我就随便贴贴代码了:

SpringApplication.run(Application.class, args);
-->
  public static ConfigurableApplicationContext run(Object source, String... args) {
    return run(new Object[] { source }, args);
  }
  public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return new SpringApplication(sources).run(args);
  }
</div>

   也就是一个静态方法,调用了构造函数创建实例,构造的参数是Object数组,这里new这个数组的时候传入了一个元素就是启动类的类对象实例(一般就是“new Object[] { Application.class” }),构造函数里调用了一个initialize方法。

  SpringApplication的initialize方法,首先在Object数组有值的情况下将数组放入一个final的类实例私有Object的Set集合中;然后用deduceWebEnvironment方法判断当前应用环境是否是web环境,判断逻辑是看Classpath是否同时存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext,缺一就认为不是。然后,调用setInitializers方法,设置类实例的私有List<ApplicationContextInitializer<?>>类型变量initializers:

 public void setInitializers(
   Collection<? extends ApplicationContextInitializer<?>> initializers) {
  this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
  this.initializers.addAll(initializers);
 }
</div>

  设置的时候会先new,也就是说这方法每次都是整体更换,不会追加。这个方法的参数都是各个模块中配置在META-INF/spring.factories中的key为org.springframework.context.ApplicationContextInitializer的值,这些类都是接口ApplicationContextInitializer<C extends ConfigurableApplicationContext>的泛型实现。

 private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
   Class<?>[] parameterTypes, Object... args) {
  ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  // Use names and ensure unique to protect against duplicates
  Set<String> names = new LinkedHashSet<String>(
    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
    classLoader, args, names);
  AnnotationAwareOrderComparator.sort(instances);
  return instances;
 }
</div>

  使用SpringFactoriesLoader.loadFactoryNames方法去取上面说的被配置的ApplicationContextInitializer的名字放进Set<String>中,并用反射创建这些名字的实例。

  setInitializers方法之后又是setInitializers,参数同上都是getSpringFactoriesInstances方法获取,只不过这次参数Class<T> type泛型类型是org.springframework.context.ApplicationListener。

   initialize方法的最后一个步是设置实例的Class<?>类型私有属性mainApplicationClass,获取设置值的方法deduceMainApplicationClass:

private Class<?> deduceMainApplicationClass() {
  try {
   StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
   for (StackTraceElement stackTraceElement : stackTrace) {
    if ("main".equals(stackTraceElement.getMethodName())) {
     return Class.forName(stackTraceElement.getClassName());
    }
   }
  }
  catch (ClassNotFoundException ex) {
   // Swallow and continue
  }
  return null;
 }
</div>

  实例化SpringApplication后调用了它的run实例方法(注意不是上面的静态方法)。一进run方法首先启动了StopWatch,这个StopWatch的功能在类的注释写可,大概意思是这是个简单的秒表,用于在开发过程中方便程序员调试性能等,非线程安全,不建议用于生产。configureHeadlessProperty设置使用Headless,对于只有远程登录使用的服务器来说这样性能要好一些。接着是加载用于这个run方法启动过程的监听器,依然是getSpringFactoriesInstances方法,这次的类型是org.springframework.boot.SpringApplicationRunListener:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
  
</div>

 SpringApplicationRunListeners(Log log,
   Collection<? extends SpringApplicationRunListener> listeners) {
  this.log = log;
  this.listeners = new ArrayList<SpringApplicationRunListener>(listeners);
 }
</div>

  先是加载所有可用监听,然后初始化SpringApplicationRunListeners对象,最后循环启动所有SpringApplicationRunListener监听。启动监听的方法:

 @Override
 public void started() {
  this.initialMulticaster
    .multicastEvent(new ApplicationStartedEvent(this.application, this.args));
 }
</div>

  ApplicationStartedEvent实例化传了两个参数,先看第一个参数this.application是怎么来的,实例的SpringApplication的run方法中,用于获取SpringApplicationRunListener,也就是前面说的getSpringFactoriesInstances被调用时:

 private SpringApplicationRunListeners getRunListeners(String[] args) {
  Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
  return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
    SpringApplicationRunListener.class, types, this, args));
 }
</div>

  getSpringFactoriesInstances方法的参数包含SpringApplication.class和this,这两个参数被传入createSpringFactoriesInstances方法:

  可以看到,是通过反射创建实例的时候,将SpringApplication中的this传进来EventPublishingRunListener构造的,然后EventPublishingRunListener构造:

public EventPublishingRunListener(SpringApplication application, String[] args) {
  this.application = application;
  this.args = args;
  this.initialMulticaster = new SimpleApplicationEventMulticaster();
  for (ApplicationListener<?> listener : application.getListeners()) {
   this.initialMulticaster.addApplicationListener(listener);
  }
 }
</div>

  最后在构造ApplicationStartedEvent时传给它的基类EventObject的protected不可序列化属性source。实例化ApplicationStartedEvent后instance.getClass()并包装为ResolvableType类型以保存类型信息,并将它和event作为参数传入SimpleApplicationEventMulticaster的multicastEvent方法。multicastEvent首先获取ApplicationListener,使用getApplicationListeners方法,这个方法中抛开对listener做了一些缓存类工作外,主要就是将事件和对应的监听器做了下是否支持的验证,返回通过了retrieveApplicationListeners中通过了supportsEvent验证的监听器集合,这里就体现出了ResolvableType的作用,它保存了类型的信息同时对泛型类型也支持。

   得到了这些匹配的监听器后,判断当前Executor是否被设置过,如果为null则同步循环执行所有:invokeListener(listener, event);如果不为null则:           

executor.execute(new Runnable() {
     @Override
     public void run() {
      invokeListener(listener, event);
     }
    });
</div>

  监听器执行的时候也会先判断是否是该由自己处理

分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • Spring boot实现热部署的两种方式详解
  • spring boot中的静态资源加载处理方式
  • Spring Boot启动过程全面解析(三)
  • Spring Boot启动过程完全解析(二)
  • Spring Boot启动过程完全解析(一)
  • Spring boot实现热部署的两种方式详解
  • spring boot中的静态资源加载处理方式
  • Spring Boot启动过程全面解析(三)
  • Spring Boot启动过程完全解析(二)
  • Spring Boot启动过程完全解析(一)

相关文章

  • 2017-05-28JDK的命令详解
  • 2017-05-28Java微信公众平台开发(11) 微信三大平台的关联
  • 2017-05-28Java二进制操作(动力节点Java学院整理)
  • 2017-05-28Java Calendar类常用示例_动力节点Java学院整理
  • 2017-05-2830分钟入门Java8之方法引用学习
  • 2017-05-28springboot整合freemarker详解
  • 2017-05-28java 中 System.out.println()和System.out.write()的区别
  • 2017-05-28Java常用数字工具类 大数乘法、加法、减法运算(2)
  • 2017-05-28详解Java 对象序列化和反序列化
  • 2017-05-28详解利用Spring加载Properties配置文件

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • JSP request.setAttribute()详解及实例
    • Java微信二次开发(三) Java微信各类型消息封装
    • spring boot ajax跨域的两种方式
    • 详解springboot整合mongodb
    • Java中终止线程的方法详解
    • springboot如何读取配置文件(application.yml)中的属性值
    • 详解Spring Aop实例之AspectJ注解配置
    • java 同步器SynchronousQueue详解及实例
    • Java去掉数字字符串开头的0三种方法(推荐)
    • Java this、final等关键字总结

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有