springboot完整启动流程

2年前 (2022) 程序员胖胖胖虎阿
307 0 0

springboot的完整启动流程

文章目录

  • springboot的完整启动流程
      • eg:
      • 计时
      • 接着创建引导上下文(Context环境)
        • 进入createBootstrapContext()中查看源代码:
      • 让当前应用进入headless模式
      • 获取所有运行监听器(为了方便所有Listener进行事件感知)
      • listeners.starting,遍历所有监听器,并且starting
      • 进入try中的执行
        • 首先是保存命令行中所有的参数
        • 准备环境信息
          • 总结准备环境信息
        • 配置需要忽略的环境信息
        • 打印banner
        • 创建IOC容器!
          • 步入createApplicationContext
          • 记录当前事件
          • 准备IOC容器信息(准备ApplicationContext)
        • 刷新IOC容器
        • 容器刷新后
        • listener通知项目启动
        • 调用所有的runners
        • 异常处理
        • Running方法
        • 创建结束

eg:

书接 springboot初始化
上文讲述了具体的spring的初始化流程,接下来就是从run方法开始讲述完整的springboot2的启动流程,版本是2.6.4

计时

在将启动器,引导器,初始化器保存和确定了主类之后,返回最准确的可用系统计时器的当前值,以毫微秒为单位。

long startTime = System.nanoTime();

springboot完整启动流程

接着创建引导上下文(Context环境)

DefaultBootstrapContext bootstrapContext = createBootstrapContext();

进入createBootstrapContext()中查看源代码:

	private DefaultBootstrapContext createBootstrapContext() {
   	DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
   	this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
   	return bootstrapContext;
   }
  • 首先创建DefaultBootstrapContext对象,保存信息。
    springboot完整启动流程

  • 接着获取之前所有的initializer并执行initialize(bootstrapContext)),来完成对引导启动器的环境设置

让当前应用进入headless模式

java.awt.headless

configureHeadlessProperty();

private void configureHeadlessProperty() {
  	System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
  			System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
  }

获取所有运行监听器(为了方便所有Listener进行事件感知)

SpringApplicationRunListeners listeners = getRunListeners(args);
  • 获取运行监听器的具体方法
  • 源代码中依然是getSpringFactoriesInstances,从spring.factories中寻找SpringApplicationRunListener.class
	private SpringApplicationRunListeners getRunListeners(String[] args) {
   	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
   	return new SpringApplicationRunListeners(logger,
   			getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
   			this.applicationStartup);
   }

springboot完整启动流程

listeners.starting,遍历所有监听器,并且starting

doWithListeners中的foreach方法。
也相当于通知所有感兴趣(需要这个信息)系统正在启动的“人”,项目正在starting

void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
		doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
				(step) -> {
					if (mainApplicationClass != null) {
						step.tag("mainApplicationClass", mainApplicationClass.getName());
					}
				});
	}

/**
进入doWithListeners
*/
	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
			Consumer<StartupStep> stepAction) {
		StartupStep step = this.applicationStartup.start(stepName);
		this.listeners.forEach(listenerAction);
		if (stepAction != null) {
			stepAction.accept(step);
		}
		step.end();
	}

进入try中的执行

首先是保存命令行中所有的参数

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

准备环境信息

ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);


//方法体👇



private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		// Create and configure the environment,有环境信息就获取=
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//getOrCreateEnvironment,返回或创建基础环境信息对象
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		//配置环境,通过命令行参数和环境信息
		ConfigurationPropertySources.attach(environment);
		//绑定信息,也就是保存工作
		listeners.environmentPrepared(bootstrapContext, environment);
		//listeners就是之前获取到的所有的RunListener,通知所有监听器当前环境准备完成
		DefaultPropertiesPropertySource.moveToEnd(environment);
		Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
				"Environment prefix cannot be set via properties.");
		bindToSpringApplication(environment);
		//绑定工作
		if (!this.isCustomEnvironment) {
			environment = convertEnvironment(environment);
		}
		
		ConfigurationPropertySources.attach(environment);
		return environment;
	}
  • 而在配置环境信息时:在更深层的调用方法configurePropertySources中有MutablePropertySources sources=environment.getPropertySources();能够获取@PropertySource,读取到外部的配置文件,也就是说此方法加载全系统中所有的配置信息!
  • 而在之后,如果有命令行中的信息也会添加进配置中
总结准备环境信息
  • 读取所有配置源的配置属性值
    configurePropertySources

  • 通知
    监听器调用listeners.environmentPrepared(bootstrapContext, environment);通知所有监听器当前环境准备完成

  • 绑定环境信息

配置需要忽略的环境信息

configureIgnoreBeanInfo(environment);

打印banner

Banner printedBanner = printBanner(environment);
springboot完整启动流程

创建IOC容器!

context = createApplicationContext();

步入createApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
		return this.applicationContextFactory.create(this.webApplicationType);
	}

根据当前项目类型(Servlet)创建
当前则会创建AnnotationConfigServletWebServerApplicationContext();
springboot完整启动流程
springboot完整启动流程

记录当前事件

context.setApplicationStartup(this.applicationStartup);

准备IOC容器信息(准备ApplicationContext)

prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

  • 进入prepareContext
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
   		ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
   		ApplicationArguments applicationArguments, Banner printedBanner) {
   	context.setEnvironment(environment);
   	//设置环境
   	postProcessApplicationContext(context);
   	//保存基本环境信息
   	applyInitializers(context);
   	listeners.contextPrepared(context);
   	bootstrapContext.close(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 AbstractAutowireCapableBeanFactory) {
   		((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
   		if (beanFactory instanceof DefaultListableBeanFactory) {
   			((DefaultListableBeanFactory) beanFactory)
   					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   		}
   	}
   	if (this.lazyInitialization) {
   		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   	}
   	// 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);
   }
  • 保存基本环境信息
    context.setEnvironment(environment);

  • IOC容器的后置处理流程
    postProcessApplicationContext(context);
    像注册一些组件,读取配置文件的资源,资源的加载器

  • 应用初始化器applyInitializers(context);
    遍历所有的ApplicationContextInitializer,调用initialize(context);来对IOC容器进行初始化(扩展工作)
    也就说之前保存的ApplicationContextInitializer在此时调用了。

  • 遍历所有的Listener调用contextPrepared(context);
    通知所有的监听器
    至此,IOC容器的上下文配置完毕。

  • 之后就是关闭bootstrapContext.close(context);

  • 然后日志信息

  • 拿到Bean工厂,注册单实例
    (项目中的参数会作为组件注册进去)
    banner也是IOC容器中的一个组件

  • 所有监听器调用contextLoaded
    通知IOC容器已经加载完毕

  • 准备工作完毕

刷新IOC容器

  • 跟随断点进入代码中
	private void refreshContext(ConfigurableApplicationContext context) {
   	if (this.registerShutdownHook) {
   		shutdownHook.registerApplicationContext(context);
   	}
   	refresh(context);
   }

注册钩子,接下来进入spring‘的核心源码refresh!
调用IOC容器的refresh

public void refresh() throws BeansException, IllegalStateException {
   	synchronized (this.startupShutdownMonitor) {
   		StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

   		// Prepare this context for refreshing.
   		//为刷新准备上下文。
   		prepareRefresh();

   		// Tell the subclass to refresh the internal bean factory.
   		//告诉子类刷新内部bean工厂。
   		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

   		// Prepare the bean factory for use in this context.
   		//准备bean工厂在这个上下文中使用。


   		prepareBeanFactory(beanFactory);

   		try {
   			// Allows post-processing of the bean factory in context subclasses.
   			//允许在context子类中对bean工厂进行后处理。
   			postProcessBeanFactory(beanFactory);

   			StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
   			// Invoke factory processors registered as beans in the context.
   			//调用在上下文中注册为bean的工厂处理器


   			invokeBeanFactoryPostProcessors(beanFactory);

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

   			// 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();
   			contextRefresh.end();
   		}
   	}
   }

在源码中有英语注释此处便不再过多赘述,深入源码改日深追!

容器刷新后

afterRefresh(context, applicationArguments);

Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
  • 监听容器启动花费了多长时间

listener通知项目启动

调用所有的runners

callRunners(context, applicationArguments);

	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}
  • 获取容器中的Runner分别是
    ApplicationRunner
    CommandLineRunner
    合并所有Runner并且按照@Order(优先级)进行排序
    遍历所有的Runner调用run方法

异常处理

  • 调用Listener的failed方法
    springboot完整启动流程

Running方法

  • 通知所有监听器的running方法,如果中间有异常,仍然会调用所有监听器的failed方法

springboot完整启动流程
running方法结束后,返回整个IOC容器。SpringBoot启动结束。

创建结束

版权声明:程序员胖胖胖虎阿 发表于 2022年11月2日 下午3:16。
转载请注明:springboot完整启动流程 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...