Spring Boot启动流程简析

这篇具有很好参考价值的文章主要介绍了Spring Boot启动流程简析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

文章首发地址

Spring Boot的设计方向

  • 可以穿件独立的Spring应用程序,可以创建可执行的jars
  • 内嵌tomcat或jetty等Servlet容器
  • 提供“入门”依赖项,以简化构建配置。尽可能自动配置Spring和第三方库
  • 提供可用于生产的功能,例如指标、运行状况检查和外部化配置

Spring Boot注解

了解基本的启动注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
    ...

AnnotationUtil.java,该类会一级一级往上找

  • @SpringBootApplication
    • @SpringBootConfiguration
      • @Configuration
        @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @Configuration
        public @interface SpringBootConfiguration {
            ...
        
    • @EnableAutoConfiguration
      • @Import(AutoConfigurationImportSelector.class)

          AutoConfigurationImportSelector是自动配置引入的选择器,它的任务是引入SpringBoot早已定义好的默认的一些Bean。包括SpringMVC涉及到的DispatcherServletAutoConfiguration和WebMVCAutoConfiguration(原本这些配置需要在bean.xml中手动配置)。——体现了Convention Over Configuration(约定优于配置)
        
        @Target(ElementType.TYPE)
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @Inherited
        @AutoConfigurationPackage
        @Import(AutoConfigurationImportSelector.class)
        public @interface EnableAutoConfiguration {
            ...
        
    • @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(classes = AutoConfigurationExcludeFilter.class)}):
      • 第一个为用户自定义的指定类型排除过滤器
      • 第二个,当用户自定义了一个@Configuration配置类,并且在spring.factories配置文件中配置时,会被第二个过滤器排除
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.TYPE)
      @Documented
      @Repeatable(ComponentScans.class)
      public @interface ComponentScan {
          ...
      

ApplicationContext

了解Spring应用上下文接口

  • 后面按顺序解读,主要需要关注SpringApplication.run()中的几个点:
    • createApplicationContext:创建应用上下文
    • prepareContext:向上下文注册一些bean
    • refreshContext:如果是tomcat,则调用AnnotationConfigServletWebServerApplicationContext的refreshContext()刷新上下文
      • 这一点在第三部分@Configuration注解中进行解读
  • 启动类
@SpringBootApplication
public class SpbtApplication {

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

}
  • SpringApplication.class构造方法
@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
        
        /**
        *SpringApplication的run方法中的SpbtApplication.class传入这里
        */
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
  • SpringApplication.class的run方法
public ConfigurableApplicationContext run(String... args) {
    // 创建StopWatch对象,用于统计run方法启动时长
    StopWatch stopWatch = new StopWatch();
    // 启动统计
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    // 配置headless 属性
    configureHeadlessProperty();
    // 获得SpringApplicationRunListener 数组
    // 该数组封装于SpringApplicationRunListeners对象的listeners中
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 启动监听,遍历SpringApplicationRunListener数组每个元素,并执行
    listeners.starting();
    try {
        // 创建ApplicationArguments对象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
        // 加载属性配置,包括所有的配置属性(如:application.properties中和外部的属性配置)
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);
        configureIgnoreBeanInfo(environment);
        // 打印Banner
        Banner printedBanner = printBanner(environment);
        // 创建容器
        context = createApplicationContext();
        // 异常报告器
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        // 准备容器,组件对象之间进行关联
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 初始化容器
        refreshContext(context);
        // 初始化操作之后执行,默认实现为空
        afterRefresh(context, applicationArguments);
        // 停止时长统计
        stopWatch.stop();
        // 打印启动日志
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);
        }
        // 通知监听器:容器启动完成
        listeners.started(context);
        // 调用ApplicationRunner和CommandLineRunner的运行方法。
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        // 异常处理
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }
    try {
        // 通知监听器:容器正在运行
        listeners.running(context);
    } catch (Throwable ex) {
        // 异常处理
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}
  • AnnotationConfigServletWebServerApplicationContext.class通过idea参看该类类图
    • 单看字段:最核心的是GenericApplicationContext.class的DefaultListableBeanFactory属性
      • 作为BeanFactory的默认实现,DefaultListableBeanFactory实现了接口所有方法(需要做一些拓展)。GenericApplicationContext借助DefaultListableBeanFactory间接实现了折现接口。因为BeanFactory在getBean之前必须获取BeanDefinition(bean的定义对象),所有后续主要关注beanDefinitionMap这个属性。
  • SpringApplication.class的rprepareContext()
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		applyInitializers(context);
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}

        /**
        *load方法传入的sources就是启动类传入run()的SpbtApplication.class,load()对这个类进行解析并添加到应用上下文中
        */
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));

		listeners.contextLoaded(context);
	}


    protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
		if (this.beanNameGenerator != null) {
			loader.setBeanNameGenerator(this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			loader.setResourceLoader(this.resourceLoader);
		}
		if (this.environment != null) {
			loader.setEnvironment(this.environment);
		}
		loader.load();
	}

@Configuration注解

了解Spring Boot是如何处理@Configuration注解的文章来源地址https://www.toymoban.com/news/detail-576719.html

  • refreshContext()中的refresh(),这个方法里就是主要的启动流程,该refresh()方法是AnnotationConfigServletWebServerApplicationContext继承的方法。如下是启动流程:
    • 准备上下文环境
    • 获取BeanFactory
    • 为BeanFactory设置属性
    • 交给子类去实现空方法
    • 调用BeanFactoryPostProcessor
      • invokeBeanFactoryPostProcessors()方法,正如方法名描述那样,是一个提供机会对BeanFactory进行一定的处理,这里一般都是注册BeanDefinition。
    • 注册BeanPostProcessors
    • 初始化Message资源
    • 初始化事件广播器
    • 交给子类去实现的空方法
    • 注册事件监听器
    • 初始化其他的单例Bean(非延迟加载的)
      • 这里通过遍历BeanDefinition对象,生成真正的实例。依赖注入等初始化操作也在这里进行。
    • 完成刷新过程,通知声明周期处理器lifecycleProcessor,同时发出ContextRefreshEvent通知finishRefresh().
@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

到了这里,关于Spring Boot启动流程简析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Boot 3.x 系列【51】启动流程 | 最后阶段

    有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot版本3.1.0 源码地址:https://gitee.com/pearl-organization/study-spring-boot3 终于启动流程到了最后一个阶段,这里主要是打印启动完成日志、调用监听器、运行 Runners 等 。

    2024年02月15日
    浏览(44)
  • 还不懂 Spring Boot 启动流程的,看这一篇就够了!

    通常,我们只需为一个类添加@SpringBootApplication注解,然后再添加一个main方法,其内固定的写法为SpringApplication.run(Application.class, args)。由此,便可启动Spring Boot服务。 具体而言,Spring Boot的启动流程包括以下几个步骤: 载入 Spring Boot 应用的启动类 根据启动类所在的包路径扫

    2024年02月05日
    浏览(44)
  • Spring Boot单元测试与热部署简析

    Spring Boot是一个用于构建独立的、生产级别的Spring应用程序的框架。它简化了Spring应用程序的开发过程,提供了自动配置和默认配置,使得开发者只需专注于业务逻辑的实现,而不用去关注繁琐的配置问题。 Spring Boot具有以下特点: 简化配置:Spring Boot通过自动配置机制来减

    2024年03月12日
    浏览(52)
  • Spring Boot 3.x 系列【49】启动流程 | 创建、准备应用上下文

    有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot版本3.1.0 源码地址:https://gitee.com/pearl-organization/study-spring-boot3

    2024年02月14日
    浏览(41)
  • 微信小程序的授权登录-Java 后端 (Spring boot)

    微信开发文档链接:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 一个可以测试的微信小程序 此微信小程序的APPID和APPscret(至开发者后台获取) 从时序图我们可以了解到流程大致分为两步: 小程序端获取code后传给Java后台 Java后台获取code后向微信后台接口

    2024年02月09日
    浏览(55)
  • “从零开始学习Spring Boot:快速搭建Java后端开发环境“

    标题:从零开始学习Spring Boot:快速搭建Java后端开发环境 摘要:本文将介绍如何从零开始学习Spring Boot,并详细讲解如何快速搭建Java后端开发环境。通过本文的指导,您将能够快速搭建一个基于Spring Boot的Java后端开发环境并开始编写代码。 正文: 一、准备工作 在开始之前,

    2024年02月15日
    浏览(58)
  • spring boot 启动报错---java: 无法访问org.springframework.boot.SpringApplication 错误的类文件:

    目录 错误提示信息: 原因: 解决办法: 具体步骤: 主要是因为 spring boot 3.0发布了 ,在创建项目时,默认为3.0 ` 但同时, spring boot 3.0 只支持jdk 17 ,在平时创建时,都喜欢使用jdk8 由于 spring boot 版本默认3.0,同时jdk选择的是 8 ,就会导致这个错误 将 spring boot 版本和jdk统一

    2024年02月15日
    浏览(50)
  • Spring Boot——Spring Boot启动原理

    2.1.1Spring Boot入口 2.1.2初始化SpringApplication 准备阶段,在程序运行之前初始化一些属性,用于在后序启动应用程序过程中。 2.1.2.1判断当前应用程序类型 2.1.2.2设置应用程序的所有初始化器(initializers) 上面这段代码主要是通过加载 “spring.factories” 配置文件中指定类型的工厂名

    2024年02月15日
    浏览(48)
  • Spring Boot 整合 Shiro(后端)

    1 Shiro 什么是 Shiro 官网: http://shiro.apache.org/ 是一款主流的 Java 安全框架,不依赖任何容器,可以运行在 Java SE 和 Java EE 项目中,它的主要作用是对访问系统的用户进行身份认证、 授权、会话管理、加密等操作。 Shiro 就是用来解决安全管理的系统化框架。 2 Shiro 核心组件 用

    2024年02月09日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包