IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot启动原理 -> 正文阅读

[Java知识库]springboot启动原理

主启动类方法:

@SpringBootApplication
public class MyJavaTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyJavaTestApplication.class, args);
    }

}

点击进入方法

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
   return new SpringApplication(primarySources).run(args);
}

先看看new SpringApplication(primarySources)里做了什么?

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断当前web容器的类型,一般返回SERVLET,标识是web类型
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
//获取META-INF/spring.factories文件中以//org.springframework.boot.Bootstrapper和
//org.springframework.boot.BootstrapRegistryInitializer为key的class
//创建对象,然后装入对象属性中;
   this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
   setInitializers((Collection) 
//获取META-INF/spring.factories文件中以
//org.springframework.context.ApplicationContextInitializer的key的对
//存入到initializers集合属性中;
getSpringFactoriesInstances(ApplicationContextInitializer.class));
//获取META-INF/spring.factories文件中以
//org.springframework.context.ApplicationListener的key的对
//存入到listeners集合属性中;
   setListeners((Collection) 
getSpringFactoriesInstances(ApplicationListener.class));
//找到main方法的启动主类class对象赋值到对象属性中。
   this.mainApplicationClass = deduceMainApplicationClass();
}

spring.factories读取了对外扩展的ApplicationContextInitializer ,ApplicationListener 对外扩展, 对类解耦(比如全局配置文件、热部署插件)
所以我们可以利用这一特性,在容器加载的各个阶段进行扩展。

上面读取到的对象getSpringFactoriesInstances(ApplicationContextInitializer.class))
存入到initializers集合中的对象

在这里插入图片描述
getSpringFactoriesInstances(ApplicationListener.class));存入到listeners集合属性中的对象
在这里插入图片描述
再看一下最重要的run方法:

public ConfigurableApplicationContext run(String... args) {
// 用来记录当前springboot启动耗时 
   StopWatch stopWatch = new StopWatch();
// 就是记录了启动开始时间 
   stopWatch.start();
//创建DefaultBootstrapContext bootstrapContext = new 
//DefaultBootstrapContext();对象,然后循环执行上面文件中赋值的bootstrapRegistryInitializers集合中对象的方法,入参就是bootstrapContext对象,利用此处我们可以扩展改变bootstrapContext对象中的一项属性值等,在这里还不知道此bootstrapContext对象的用处。
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// 它是任何spring上下文的接口, 所以可以接收任何ApplicationContext实现 
   ConfigurableApplicationContext context = null;
// 开启了Headless模式,暂时不知道此模式的作用 
   configureHeadlessProperty();
//去spring.factroies中读取了 org.springframework.boot.SpringApplicationRunListener为key的对象,默认是EventPublishingRunListener对象,然后封装进SpringApplicationRunListeners 对象中,此对象还是比较有用的,用来发布springboot启动进行中的各个状态的事件,上面方法中读取到的监听器就可以监听到这些事件,所以可以运用这些特性进行自己的扩展。
   SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布1.ApplicationStartingEvent事件,在运行开始时发送 ,发送springboot启动开始事件;
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
//根据启动项目命令所带的参数创建applicationArguments 对象
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//初始化环境变量:读取系统环境变量、发送了一个ApplicationEnvironmentPreparedEvent事件,利用相关监听器来解析项目中配置文件中的配置,(EnvironmentPostProcessorApplicationListener
监听器解析的配置文件)
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 忽略beaninfo的bean 
      configureIgnoreBeanInfo(environment);
// 打印Banner 横幅 
      Banner printedBanner = printBanner(environment);
//根据webApplicationType 容器类型,创建对应的spring上下文,一般是AnnotationConfigServletWebServerApplicationContext;
      context = createApplicationContext();
//给spring上下文赋值DefaultApplicationStartup对象
      context.setApplicationStartup(this.applicationStartup);
//预初始化上下文,这里做了给上下文添加environment对象,上面获取到的initializers集合中的ApplicationContextInitializer对象执行其入参为上下文initialize方法,对上下文做编辑,所以此处我们可以做扩展;发送ApplicationContextInitializedEvent容器初始化事件;发送BootstrapContextClosedEvent事件;最重要的一个方法就是把启动配置类注册成了beanDefinition;发送ApplicationPreparedEvent事件,并把listeners集合属性中的事件添加到上下文中;
	prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
	//刷新容器:和spring中的refresh()方法的作用是一样的,主要的作用就是读取所有的bean转成beanDefinition然后再创建bean对象;不过这个容器重写了其中的onRefresh()方法,在此方法中,创建了springboot内置的tomcat对象并进行启动;接下来特别说明一下这个内置tomcat
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
//打印启动时间;
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
//发布容器启动事件ApplicationStartedEvent;发布AvailabilityChangeEvent容器可用实践;
      listeners.started(context);
//执行容器中ApplicationRunner、ApplicationRunner类型对象的run方法
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }

   try {
//发布ApplicationReadyEvent容器已经正常事件;
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

prepareEnvironment方法

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // 根据webApplicationType 创建Environment  创建就会读取: java环境变量和系统环境变量
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   // 将命令行参数读取环境变量中
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   // 将@PropertieSource的配置信息 放在第一位, 因为读取配置文件@PropertieSource优先级是最低的
   ConfigurationPropertySources.attach(environment);
   // 发布了ApplicationEnvironmentPreparedEvent 的监听器  读取了全局配置文件
   listeners.environmentPrepared(environment);
   // 将所有spring.main 开头的配置信息绑定SpringApplication
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
            deduceEnvironmentClass());
   }
   //更新PropertySources
   ConfigurationPropertySources.attach(environment);
   return environment;
}

?prepareContext
?预初始化上下文

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
      SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
   // 拿到之前读取到所有ApplicationContextInitializer的组件, 循环调用initialize方法
   applyInitializers(context);
   // 发布了ApplicationContextInitializedEvent
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // 获取当前spring上下文beanFactory (负责创建bean)
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   // 在Spring下 如果出现2个重名的bean, 则后读取到的会覆盖前面
   // 在SpringBoot 在这里设置了不允许覆盖, 当出现2个重名的bean 会抛出异常
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   // 设置当前spring容器是不是要将所有的bean设置为懒加载
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   // 读取主启动类,将它注册为BD、就像我们以前register(启动类);一个意思 (因为后续要根据配置类解析配置的所有bean)
   load(context, sources.toArray(new Object[0]));
   //4.读取完配置类后发送ApplicationPreparedEvent。
   listeners.contextLoaded(context);
}

1.初始化SpringApplication 从spring.factories 读取 listener ApplicationContextInitializer 。
2.运行run方法
3.读取 环境变量 配置信息…
4.创建springApplication上下文:ServletWebServerApplicationContext
5.预初始化上下文 : 读取启动类
6.调用refresh 加载ioc容器
7.加载所有的自动配置类
8.创建servlet容器
9.ps.在这个过程中springboot会调用很多监听器对外进行扩展

看一下内置tomcat如何启动的:
Springboot的spring容器ServletWebServerApplicationContext对象重新了refresh()方法中的onRefresh()放用来启动tomcat

@Override
protected void onRefresh() {
   super.onRefresh();
   try {
//创建tomcat
      createWebServer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
   }
}

createWebServer创建tomcat的方法(也可以是Jetty,根据配置)

private void createWebServer() {
   WebServer webServer = this.webServer;
   ServletContext servletContext = getServletContext();
//如果servletContext 为null说明是内置tomcat,不为null,则使用的是外置tomcat,这个servletContext 是tomcat创建传入的;
   if (webServer == null && servletContext == null) {
      StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
//获取servlet容器创建工厂,tomcat的是TomcatServletWebServerFactory
      ServletWebServerFactory factory = getWebServerFactory();
      createWebServer.tag("factory", factory.getClass().toString());
//创建内置tomcat对象,并启动;getSelfInitializer()这个方法也是很重要的,返回的是ServletWebServerApplicationContext#selfInitialize方法的引用函数,其作用是tomcat启动时会回调此方法,并传入servletContext对象,进行DispacherServlet添加到servletContext中,把当前spring容器和filter过滤器也添加到servletContext中
      this.webServer = factory.getWebServer(getSelfInitializer());
      createWebServer.end();
      getBeanFactory().registerSingleton("webServerGracefulShutdown",
            new WebServerGracefulShutdownLifecycle(this.webServer));
      getBeanFactory().registerSingleton("webServerStartStop",
            new WebServerStartStopLifecycle(this, this.webServer));
   }
   else if (servletContext != null) {
      try {
         getSelfInitializer().onStartup(servletContext);
      }
      catch (ServletException ex) {
         throw new ApplicationContextException("Cannot initialize servlet context", ex);
      }
   }
   initPropertySources();
}

selfInitializ方法

private void selfInitialize(ServletContext servletContext) throws ServletException {
//把spring容器和servletContext进行相互引用
   prepareWebApplicationContext(servletContext);
   registerApplicationScope(servletContext);
   WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
//servletContext中添加Dispacherservlet和多个fileter
   for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
      beans.onStartup(servletContext);
   }
}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 18:38:23  更:2022-07-20 18:38:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 7:22:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码