springboot启动流程详解_网页解析的详细过程

springboot启动流程详解_网页解析的详细过程SpringBoot启动流程和加载顺序,以及思考心得


Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺



前言

此文章讲解SpringBoot中配置Bean的几种形式,以及在SpringBoot启动流程中的先后顺序。


一、SpringBoot特性

Spring核心特性和概念:
在这里插入图片描述
SpringBoot核心特型:

  1. 约定大于配置
    提供了默认的编码Bean配置扫描机制,默认的WebServer启动机制,默认的三方包Bean加载配置机制等等。
  2. Java代码定义配置Bean

二、配置形式

1.应用主入口

@SpringBootApplication
@ImportResource(locations = {"classpath:spring-config.xml"})
public class TestWebApp {
    public static void main(String[] args) {
        SpringApplication.run(TestWebApp.class, args);
    }
}

@SpringBootApplication注解是一个组合注解,主要由@SpringBootConfiguration,@EnableAutoConfiguration和@ComponentScan这三个注解组合而成。
因此@SpringBootApplication注解主要作为一个配置类,能够触发包扫描和自动配置的逻辑,从而使得SpringBoot的相关bean被注册进Spring容器。

1.定义Bean

@RestController
public class HelloController {
}

@Service
public class TestService {
}

2.Configuration类配置

@Configuration
public class TestConfiguration {
    @Bean
    public TestConfigBean testConfig() {
        return new TestConfigBean();
    }
}

3. Spring xml配置

    <bean id="TestWebView" class="com.wei.spring.app.testweb.bean.TestWebView">
    </bean>

3.AutoConfiguration类配置

在二方包模块中的resources/META-INF/spring.factories文件中按照如下格式注册AutoConfiguration类

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.wei.spring.autoconfig.TestAutoConfigurationA,\
  com.wei.spring.autoconfig.TestAutoConfigurationB

二方包模块中定义AutoConfiguration类

@Configuration
public class TestAutoConfigurationA {
    @Bean
    public AutoBeanA autoBeanA() {
        return new AutoBeanA();
    }
}

@Configuration
@AutoConfigureAfter(TestAutoConfigurationA.class)
public class TestAutoConfigurationB {
    @Bean
    @ConditionalOnBean(value = AutoBeanA.class)
    public AutoBeanB autoBeanB(AutoBeanA autoBeanA) {
        return new AutoBeanB(autoBeanA);
    }
}

在主应用的pom.xml引入二方包,spring就会进行自动装载

        <dependency>
            <groupId>com.test</groupId>
            <artifactId>spring-demo-autoconfig</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

三、启动流程

1.流程图

在这里插入图片描述

  1. SpringApplicationRunListener::starting发布开始启动事件
    1. ApplicationListener::onApplicationEvent发布ApplicationStartingEvent
  2. prepareEnvironment准备环境,创建Environment,配置PropertySources和Profile
    1. getOrCreateEnvironment获取Environment
    2. configureEnvironment配置Environment
      1. configurePropertySources设置PropertySources
      2. configureProfiles设置Profile
    3. SpringApplicationRunListener::environmentPrepared发布environmentPrepared
      1. ApplicationListener::onApplicationEvent发布ApplicationEnvironmentPreparedEvent
  3. createApplicationContext创建ApplicationContext
  4. prepareContext在Context执行前,进行准备工作,配置
    1. ApplicationContextInitializer::initialize扩展点,调用自定义注册的ApplicationContextInitializer的initialize方法,对ApplicationContext进行自定义设置
    2. SpringApplicationRunListener::contextPrepared发布contextPrepared
      1. ApplicationListener::onApplicationEvent发布ApplicationContextInitializedEvent
    3. load(ConfigurableApplicationContext)装载ApplicationContext,包括启动类
      1. BeanDefinitionLoader::load加载Bean定义(SpringBootApplication主程序类)
    4. SpringApplicationRunListener::contextLoaded发布事件
      1. ApplicationListener::onApplicationEvent发布ApplicationPreparedEvent
  5. refreshContext刷新Context,Context主流程
    1. prepareRefresh准备刷新的上下文环境,系统属性,环境变量
    2. obtainFreshBeanFactory获取BeanFactory并初始化,Bean配置读取解析,定制功能包括@Qualifier和@Autowired
    3. prepareBeanFactory对BeanFactory进行功能填充,SpEL,属性编辑器,AspectJ等
    4. postProcessBeanFactory后置处理BeanFacotry,扩展点,空函数
    5. invokeBeanFactoryPostProcessors激活各种BeanFacotry处理器,操作BeanDefinition
      1. 按照硬编码,PriorityOrdered注解,Ordered注解,以及剩余的的BeanDefinitionRegistryPostProcessor::postProcessBeanDefinitionRegistry
        1. 执行SpringBoot扩展注册的ConfigurationClassPostProcessor,用于实现SpringBoot核心的代码注册Bean的功能
          1. 循环解析ConfigurationClass
            1. parse解析Configuration类
              1. 解析Configuration类
                1. processMemberClasses解析成员嵌套类
                2. processPropertySource处理@PropertySource
                3. componentScanParser.parse处理@ComponentScan
                  1. 递归解析扫描到的Configuration类.
                4. processImports处理@Import
                5. Process ImportedResource处理@ImportedResource
                6. Proces @Bean methods处理@Bean方法
                7. Process default methods on interfaces解析接口上的@Bean方法
                8. Process superclass, if any寻找父类,递归处理
              2. deferredImportSelectorHandler.process执行延迟ImportSelector
            2. loadBeanDefinitions将所有Bean定义注册到BeanFactory中
              循环处理扫描到的所有Configuration类,将Configuration类中定义的Bean,ImportResource的xml文件的Bean,注册Bean到BeanFacotry
              Configuration类的顺序按照扫描到的顺序,顺序有以下规则:
              a.当前Configuration类ComponentScan扫描扫的Configuration类优先于当前Configuration类。
              b.当前Configuration类优先于延时SelectImport的Configuration类(AutoConfiguration类)
              1. registerBeanDefinitionForImportedConfigurationClass
              2. loadBeanDefinitionsForBeanMethod
              3. loadBeanDefinitionsFromImportedResources
              4. loadBeanDefinitionsFromRegistrars
      2. 执行BeanDefinitionRegistryPostProcessor::postProcessBeanFactory
      3. 按照硬编码,PriorityOrdered注解,Orderd注解,以及剩余的BeanFactoryPostProcessor::postProcessBeanFactory
    6. registerBeanPostProcessors注册拦截Bean创建处理器,会在getBean中创建Bean时调用
      1. 顺序注册PriorityOrdered的BeanPostProcessor
      2. 顺序注册Ordered的BeanPostProcessor
      3. 注册普通的BeanPostProcessor
      4. 注册MergedBeanDefinitionPostProcessor
    7. onRefresh扩展点
      1. createWebServer启动WebServer
    8. registerListeners
    9. finishBeanFactoryInitialization实例化ApplicationContext中剩余未实例化的所有Bean
    10. finishRefresh发布事件
      1. ApplicationListener::onApplicationEvent发布ServletWebServerInitializedEvent和ContextRefreshedEvent和DubboApplicationStateEvent
  6. afterRefresh扩展点,在ApplicationContext Refresh完后触发执行
  7. SpringApplicationRunListener::started发布程序已经启动事件
    1. ApplicationListener::onApplicationEvent发布ApplicationStartedEvent和AvailabilityChangeEvent
  8. callRunners扩展点,当程序已经启动后,调用自定义注册的ApplicationRunner和CommandLineRunner,执行运行代码
  9. SpringApplicationRunListener::running发布程序运行事件
    1. ApplicationListener::onApplicationEvent发布ApplicationReadyEvent和AvailabilityChangeEvent

3.Bean定义加载顺序

  1. 主应用的Bean定义先于AutoConfiguration中的Bean定义加载
  2. 主应用扫描的Bean定义之间,没有明确的加载顺序
  3. AutoConfiguration之间可以通过注解定义加载顺序。(注解@AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureOrder)

思考总结

ConditionalOnMissingBean和ConditionalOnBean
这两个注解只在当前已经扫描加入BeanFacotry的所有Bean中判断某个Bean定义是否存在,所以依赖于Bean定义的扫描加载顺序,不同加载顺序得到判断结果完全不同。
基于上面所说的Bean定义加载顺序,有几下实践建议:

  1. 不能在主应用的代码中使用注解,首先AutoConfiguration还没加载(判断AutoConfiguration中的Bean必定是不存在),此时用注解判断AutoCnofiguration定义的Bean没有意义。其次如果用于判断主应用中的Bean定义是否存在,由于主应用Bean加载顺序不确定,代码和xml配置文件修改会导致Bean扫描加载顺序变动引发问题,存在风险不确定性。最重要的是,主应用代码是由当前开发团队编写,代码已经确定Bean存在不存在,不需要在用这两个注解判断,注解反而给后续的人阅读代码引起误解,存在代码不清晰和不确定性。(代码编写追求的特点之一是清晰性。)
  2. 在AutoConfiguration中使用这两个注解,如果要判断主应用中是否有定义Bean那么没问题。如果是判断其他AutoConfiguration是否有Bean,那么要用注解确定AutoConfiguration的顺序。最常见稳妥的使用场景,是进行兜底默认配置,判断主应用有没有Bean,如果没有,那么AutoConfiguration进行兜底创建Bean定义。

异步线程调用Dubbo Service服务引发Bean加载死锁
Spring加载Bean会加锁。主线程加载Bean时,如果此时有异步线程进行Dubbo调用,或者加载Bean,会导致两个线程死锁。
比如,在@PostConstruct中启动异步线程调用Dubbo服务初始化缓存。
所以,如果是服务运行必要的初始化内容,一定要预先初始化的,必须在Spring初始化线程中同步执行。
如果要异步初始化,则要在Spring启动完成后再进行,比如订阅ApplicationStartedEvent事件再执行。
保证同时只有一个线程在进行Spring加载Bean。

最后

要熟悉SpringBoot启动流程,以及各种配置在启动中的先后顺序,才能在开发中得心应手,避免踩坑。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/171537.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 二叉树前序遍历、中序遍历、后序遍历、层序遍历的直观理解

    二叉树前序遍历、中序遍历、后序遍历、层序遍历的直观理解0.写在最前面希望大家收藏:本文持续更新地址:https://haoqchen.site/2018/05/23/go-through-binary-tree/    复习到二叉树,看到网上诸多博客文章各种绕,记得头晕。个人觉得数学、算法这些东西都是可以更直观简洁地表示,然后被记住的,并不需要靠死记硬背。本文的程序基本来源于《大话数据结构》,个人感觉是一本非常好的书,推荐去看。…

    2022年6月12日
  • SQL的多表查询

    SQL的多表查询

    2021年7月20日
  • mysql的慢查询日志_一条sql查询很慢怎么去优化

    mysql的慢查询日志_一条sql查询很慢怎么去优化MySQL慢查询日志总结慢查询日志概念   MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,则会被记录到慢查询日志中。long_query_time的默认值为10,意思是运行10S以上的语句。默认情况下,Mysql数据库并不启动慢查询日志,需要我们手动来设置这个参数,当然…

    2022年10月14日
  • android strictmode有什么作用,Android StrictMode policy

    android strictmode有什么作用,Android StrictMode policy问题RunningintosomeANRissueswithanandroidapplicationsoIimplementedtheStrictModepolicies.Neverusedthisbeforesohopefullysomeonecanhelpexplainthefollowing:Whydoesthelogshow2…

    2022年6月3日
  • 小米解bl锁跳过168小时_xiaomi redmi 红米秒解BL工具分享支持小米红米机型秒解BL跳过168小时[通俗易懂]

    小米解bl锁跳过168小时_xiaomi redmi 红米秒解BL工具分享支持小米红米机型秒解BL跳过168小时[通俗易懂]目前小米的新机,官方风控都默认绑定7天也就是168小时才能解锁BL,部分账号需要绑定15天才能满足条件,导致很多爱玩机的小伙伴被拒门外。并不是所有人都愿意等待官方解锁时候,而跳过168小时解锁,也成为了很多小伙伴希望的事情。本工具来自ROM乐园技术大神分享,经测试部分版本可秒解BL,无需等待,但并不是百分百才能成功。如果你官方工具解锁失败,不妨试试这个解锁工具,建议更换1个小米账号,再使用此工具解锁BL看看————————————————unlockBootloaderxiaomi…

    2022年6月6日
  • 网页幻灯片轮播代码_怎么快速实现对幻灯片的统一修改

    网页幻灯片轮播代码_怎么快速实现对幻灯片的统一修改   NetCMS有两种幻灯片显示方式:Flash幻灯片和轮换幻灯片。Flash幻灯片是通过将图片新闻中的图片合成Flash后再在页面上显示。轮换幻灯片则是使用脚本进行控制(准确地说,是使用VBScript)。   其实,这两种显示形式差不多,只不过Flash幻灯片是通过Flash实现图片的过渡效果,而轮换幻灯片是利用IE提供的Filter属性实现图片过渡效果的。   鉴于轮换幻灯片

    2022年9月30日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号