Springboot源码——七大步骤详解

概要

Spring boot启动时的七大步骤如下:

  1. 获取并启动监听器
  2. 准备应用程序环境
  3. 控制台打印Banner
  4. 创建应用上下文对象
  5. 准备应用上下文
  6. 刷新应用程序上下文
  7. 后续处理

测试入口:

//标识启动类,它是一个复合注解,标识使用自动装配、通过扫描注解注入bean
@SpringBootApplication    // ===> 自动装载进【入口 - 2  !】
public class Hibernate52Application {

	public static void main(String[] args)  {
		SpringApplication.run(Hibernate52Application.class, args);  // ===>  boot启动进 【入口 - 1 !】
	}

}

主流程代码:

//	run方法(七大步骤)
//1、获取并启动监听器从spring.factories文件中加载
//2、准备应用程序环境
//3、打印banner
//4、创建应用上下文  AnnotationConfigServletWebServerApplicationContext
//5、准备 applicationContext
//6、刷新context-- call  refresh  mothod(核心)
//7、上下文刷新后触发(空方法)
public ConfigurableApplicationContext run(String... args) {
	//计时器
	StopWatch stopWatch = new StopWatch();
	stopWatch.start(); //开始计时
	//  创建启动上下文对象,存放配置参数环境信息等
	DefaultBootstrapContext bootstrapContext = createBootstrapContext();
	// 定义容器
	ConfigurableApplicationContext context = null;
	// awt
	configureHeadlessProperty();
	// 第一步:获取并启动监听器    已经在上面new的步骤放到缓存中了,现在取出来
	SpringApplicationRunListeners listeners = getRunListeners(args);  // ===>  自定义listener
	//向监听器发布start事件,【发布订阅模式,也叫生产者消费者模式】
	// 默认会有一个EventPublishingRunListener
	listeners.starting(bootstrapContext, this.mainApplicationClass);
	try {
		// 对命令行参数包装成一个对象(ApplicationArguments),比如:--spring.profiles.active=dev
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);  // ===>
		//第二步:准备应用程序环境(配置文件application.yml就是这里加载的)
		ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);  // ===>
		// 配置要忽略哪些bean
		configureIgnoreBeanInfo(environment);
		//第三步: 打印banner
		Banner printedBanner = printBanner(environment); // ===>
		// 第四步:【重点】创建应用上下文 servlet对应的是 AnnotationConfigServletWebServerApplicationContext
		context = createApplicationContext(); // ===>
		context.setApplicationStartup(this.applicationStartup);
		//第五步:准备 applicationContext
		// refresh将会使用Annotation方式扫描启动main上的@ComponentScan。
		prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
		//第六步:context,ioc的领地。bean在这里被创建起来
		// tomcat容器被启动起来~~~~
		// 它藏在 ioc 的 onRefresh 这一步里
		refreshContext(context);// ===>  进入ioc的领地,刷新 spring ioc 容器, 加载和扫描所有的bean
		//第七步:创建完成后的扫尾工作
		afterRefresh(context, applicationArguments);
		stopWatch.stop();//停止计时
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
		}
		// 发布started事件
		listeners.started(context);
		//执行runner的run方法,它允许你在容器启动后做一些事情
		callRunners(context, applicationArguments); //===>
	} catch (Throwable ex) {
		// 异常处理
		handleRunFailure(context, ex, listeners);
		throw new IllegalStateException(ex);
	}

	try {
		// 触发running事件
		listeners.running(context);
	} catch (Throwable ex) {
		handleRunFailure(context, ex, null);
		throw new IllegalStateException(ex);
	}
	// 返回最终构建的容器对象
	return context;
}

获取并启动监听器

这里的监听器,就是监听SpringBoot的整个启动流程,实现了SpringApplicationRunListener接口,就可以完成监听,在启动的不同阶段,可以执行到不同的方法

在这里插入图片描述

//这里的启动监听就是我们需要监听SpringBoot的启动流程监听,实现SpringApplicationRunListener类即可监听
// 获取spring.factories中 key为SpringApplicationRunListener的对象实例。
private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[]{SpringApplication.class, String[].class};
	// 通过从 spring.factories 中获取 SpringApplicationRunListener 类型的配置类
	// 在new阶段已经放到缓存中了。
	return new SpringApplicationRunListeners(logger,
			getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
			this.applicationStartup);  // ===> getSpringFactoriesInstances
}

查看具体SpringApplicationRunListener都有哪些方法

public interface SpringApplicationRunListener {

	//当调用run方法后会立即调用,可以用于非常早期的初始化
	default void starting(ConfigurableBootstrapContext bootstrapContext) {
		starting();
	}

	@Deprecated
	default void starting() {
	}

	//环境准备好之后调用
	default void environmentPrepared(ConfigurableBootstrapContext bootstrapContext,
			ConfigurableEnvironment environment) {
		environmentPrepared(environment);
	}

	@Deprecated
	default void environmentPrepared(ConfigurableEnvironment environment) {
	}

	//在加载资源之前,ApplicationContex准备好之后调用
	default void contextPrepared(ConfigurableApplicationContext context) {
	}

	//在加载应用程序上下文但在其刷新之前调用
	default void contextLoaded(ConfigurableApplicationContext context) {
	}

	/**
	 * 上下文已经刷新且应用程序已启动且所有{@link CommandLineRunner commandLineRunner}
	 * 和{@link ApplicationRunner ApplicationRunners}未调用之前调用
	 */
	default void started(ConfigurableApplicationContext context) {
	}

	/**
	 * 当应用程序上下文被刷新并且所有{@link CommandLineRunner commandLineRunner}
	 * 和{@link ApplicationRunner ApplicationRunners}都已被调用时,在run方法结束之前立即调用。
	 */
	default void running(ConfigurableApplicationContext context) {
	}

	
	//在启动过程发生失败时调用
	default void failed(ConfigurableApplicationContext context, Throwable exception) {
	}

准备应用程序环境

创建并配置SpringBoot应用将要使用的Environment
在这里插入图片描述


//创建并配置SpringBoot应用将要使用的Environment
// 过程如下:
//	1、创建配置环境 ConfigurableEnvironment
//	2、加载属性文件资源
//	3、配置监听
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
												   DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
	// 根据不同的web类型创建不同实现的Environment对象,servlet环境下返回 StandardServletEnvironment
	ConfigurableEnvironment environment = getOrCreateEnvironment();  // ===>
	// 命令行参数 启动的时候 --spring.profiles.active=dev   等等那些
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	ConfigurationPropertySources.attach(environment);
	// 发送环境已准备完成事件,listerners= 前面准备过的监听器
	listeners.environmentPrepared(bootstrapContext, environment);  // ===> 发布事件
	DefaultPropertiesPropertySource.moveToEnd(environment); // 如果有defaultProperties配置移到最后
	// 其他参数
	configureAdditionalProfiles(environment);
	// 绑定环境到SpringApplication
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
		environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
				deduceEnvironmentClass());
	}
	ConfigurationPropertySources.attach(environment);
	return environment; //返回去
}

控制台打印banner

在这里插入图片描述

private Banner printBanner(ConfigurableEnvironment environment) {
	// banner模式,可以是console、log、off
	if (this.bannerMode == Banner.Mode.OFF) {
		return null;
	}
	ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
			: new DefaultResourceLoader(null);
	SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
	//日志打印banner
	if (this.bannerMode == Mode.LOG) {
		return bannerPrinter.print(environment, this.mainApplicationClass, logger);
	}
	//控制台打印banner
	return bannerPrinter.print(environment, this.mainApplicationClass, System.out);  // ===> go
}
Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
	//获取banner.txt
	Banner banner = getBanner(environment);  // ===>
	//开始打印
	banner.printBanner(environment, sourceClass, out); // ===>
	return new PrintedBanner(banner, sourceClass);
}
private Banner getBanner(Environment environment) {
	Banners banners = new Banners();
	banners.addIfNotNull(getImageBanner(environment)); // 图片banner,没有,jwt显示
	banners.addIfNotNull(getTextBanner(environment)); // ===>  文字banner,走ResourceBanner
	if (banners.hasAtLeastOneBanner()) {
		return banners; //只要有自定义的,返回
	}
	if (this.fallbackBanner != null) {
		return this.fallbackBanner;
	}
	return DEFAULT_BANNER; // ===>  默认使用SpringBootBanner()实现
}

创建应用上下文对象

protected ConfigurableApplicationContext createApplicationContext() {
	return this.applicationContextFactory.create(this.webApplicationType); // ===> create
}

其中applicationContextFactory变量如下:

private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;

org.springframework.boot.ApplicationContextFactory#DEFAULT

//无论哪一种,都是Annotation的,也就是boot都是注解+扫包
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
	try {
		switch (webApplicationType) {
		case SERVLET:
			return new AnnotationConfigServletWebServerApplicationContext(); // 这里!注意看类型~
		case REACTIVE:
			return new AnnotationConfigReactiveWebServerApplicationContext();
		default:
			return new AnnotationConfigApplicationContext();
		}
	}
	catch (Exception ex) {
		throw new IllegalStateException("Unable create a default ApplicationContext instance, "
				+ "you may need a custom ApplicationContextFactory", ex);
	}
};

准备应用上下文

org.springframework.boot.SpringApplication#prepareContext

/**
 * Spring容器准备
 */
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
							ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
							ApplicationArguments applicationArguments, Banner printedBanner) {
	//一堆的设置
	context.setEnvironment(environment);//设置环境
	postProcessApplicationContext(context);//设置上下文
	// 执行所有ApplicationContextInitializer对象的initialize方法(这些对象是通过读取spring.factories加载)
	applyInitializers(context);//设置初始化工作
	// 发布上下文准备完成事件到所有监听器
	listeners.contextPrepared(context);//触发监听器

	// 这句容易误解,其实是关闭bootstrap,启用ApplicationContext
	// 前期bootstrapContext用来处理yml等配置文件和环境,现在它的使命已经完成
	// 下面交给ApplicationContext去加载bean,进入了spring ioc的领地。
	// https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/DefaultBootstrapContext.html
	// "when the BootstrapContext is closed and the ApplicationContext has been prepared"
	bootstrapContext.close(context);
	if (this.logStartupInfo) { //日志操作
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);  // The following profiles are active: dev
	}
	// 获取工厂 DefaultListableBeanFactory
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	//参数对象
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		//banner对象
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof DefaultListableBeanFactory) {
		//是否覆盖bean
		((DefaultListableBeanFactory) beanFactory)
				.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	if (this.lazyInitialization) { //是否懒加载
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	// Load the sources
	Set<Object> sources = getAllSources();  // 所有的启动类,就是main函数启动时传进来的class
	Assert.notEmpty(sources, "Sources must not be empty");
	//注解加载 这里是对Hibernate52Application 进行加载
	load(context, sources.toArray(new Object[0])); 
	// 发送上下文加载完成事件
	listeners.contextLoaded(context);
}

org.springframework.boot.BeanDefinitionLoader#load(java.lang.Class<?>)


private void load(Class<?> source) {
	if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
		// Any GroovyLoaders added in beans{} DSL can contribute beans here
		GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
		((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());
	}
	if (isEligible(source)) {
		//将启动类注册到BD里去,source就是我们的启动类,一个@Configration
		this.annotatedReader.register(source);  // BeanDefinition创建完成,往回走
	}
}

刷新应用程序上下文

ioc容器初始化,同时启动tomcat

//核心方法
private void refreshContext(ConfigurableApplicationContext context) {
	// 往下走!
	if (this.registerShutdownHook) {
		try {
			context.registerShutdownHook();
		} catch (AccessControlException ex) {
			// Not allowed in some environments.
		}
	}
	// ioc,refresh
	refresh((ApplicationContext) context); // ===>
}

最终会调用到org.springframework.context.support.AbstractApplicationContext#refresh方法,该方法会调用到,org.springframework.context.support.AbstractApplicationContext#onRefresh,这个方法是空方法,子类会对他进行实现,而tomcat的启动就是在子类的实现中触发的。

在这里插入图片描述

后续处理

/**
 * Called after the context has been refreshed.
 *
 * @param context the application context
 * @param args    the application arguments
 */
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值