Spring Boot启动流程简析

文章首发地址

Spring Boot的设计方向

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

Spring Boot注解

了解基本的启动注解

java 复制代码
@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

        java 复制代码
        @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(约定优于配置)
        
        java 复制代码
        @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配置文件中配置时,会被第二个过滤器排除
      java 复制代码
      @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注解中进行解读
  • 启动类
java 复制代码
@SpringBootApplication
public class SpbtApplication {

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

}
  • SpringApplication.class构造方法
java 复制代码
@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方法
java 复制代码
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()
java 复制代码
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注解的

  • refreshContext()中的refresh(),这个方法里就是主要的启动流程,该refresh()方法是AnnotationConfigServletWebServerApplicationContext继承的方法。如下是启动流程:
    • 准备上下文环境
    • 获取BeanFactory
    • 为BeanFactory设置属性
    • 交给子类去实现空方法
    • 调用BeanFactoryPostProcessor
      • invokeBeanFactoryPostProcessors()方法,正如方法名描述那样,是一个提供机会对BeanFactory进行一定的处理,这里一般都是注册BeanDefinition。
    • 注册BeanPostProcessors
    • 初始化Message资源
    • 初始化事件广播器
    • 交给子类去实现的空方法
    • 注册事件监听器
    • 初始化其他的单例Bean(非延迟加载的)
      • 这里通过遍历BeanDefinition对象,生成真正的实例。依赖注入等初始化操作也在这里进行。
    • 完成刷新过程,通知声明周期处理器lifecycleProcessor,同时发出ContextRefreshEvent通知finishRefresh().
java 复制代码
@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();
			}
		}
	}
相关推荐
2401_8576100325 分钟前
Spring Boot框架:电商系统的技术优势
java·spring boot·后端
希忘auto42 分钟前
详解MySQL安装
java·mysql
冰淇淋烤布蕾1 小时前
EasyExcel使用
java·开发语言·excel
拾荒的小海螺1 小时前
JAVA:探索 EasyExcel 的技术指南
java·开发语言
Jakarta EE1 小时前
正确使用primefaces的process和update
java·primefaces·jakarta ee
马剑威(威哥爱编程)1 小时前
哇喔!20种单例模式的实现与变异总结
java·开发语言·单例模式
java—大象2 小时前
基于java+springboot+layui的流浪动物交流信息平台设计实现
java·开发语言·spring boot·layui·课程设计
ApiHug2 小时前
ApiSmart x Qwen2.5-Coder 开源旗舰编程模型媲美 GPT-4o, ApiSmart 实测!
人工智能·spring boot·spring·ai编程·apihug
魔道不误砍柴功2 小时前
探秘Spring Boot中的@Conditional注解
数据库·spring boot·oracle
杨哥带你写代码2 小时前
网上商城系统:Spring Boot框架的实现
java·spring boot·后端