Spring Boot启动代码分析

       Spring Boot是工作中最常用的一个框架,可以通过一个简单的main函数作为入口,一直想研究下这个main函数背后都做了什么,趁着今天有时间,跟一下代码。

       做过Spring Boot项目的人都熟悉,我们通常会在main函数中调用SpringApplication的run方法,传入的参数有两个,一个是当前主类的class,另一个是main函数的参数String[] args,这个方法是有返回参数的,返回的类型是ConfigurableApplicationContext,然后继续往下走,首先new了一个SpringApplication对象,构造函数入参是主类的class所组成的数组,在这个构造函数中干了以下几件事。

this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();

一是设置了本类的resourceLoader,这里边传过来的是null,第三行代码设置了primarySources,第四行设置了webApplicationType,默认是Servlet类型,还支持Reactive类型的,第五行和第六行分别设置了初始化和一些监听器,注意看内部方法getSpringFactoriesInstances,它传入的参数是class类型,该方法最终会从META-INF/spring.factories读取默认的配置,这个文件里边是一对对的配置,key是接口,value是逗号分割的实现类,最终会生成一个map,然后根据传入的class从map中获取对应的实现类,第七行设置了标识主类的属性值。在创建完SpringApplication之后,就该进入到run方法中了,具体代码可参考 SpringApplication之run方法

       我们直接看第9行,这一行做的事是根据webApplicationType来初始化一个ConfigurableEnvironment的具体实现对象,Servlet对应的是StandardServletEnvironment,并将main函数传过来的配置参数加入添加到Environment中。第13行,也是根据webApplicationType初始化了一个ApplicationContext的具体实现类,这里边是AnnotationConfigServletWebServerApplicationContext,此时只是一个空对象。

        第17行是prepareContext方法,参数分别为context,environment,listener,applicationArguments,printedBanner,代码如下:

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);
		}
		// 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);
	}

这个函数首先将environment设置到context的environment属性。然后是postProcessApplicationContext方法,这方法的注释为应用任何相关的后处理到ApplicationContext,子类可以根据需要应用额外的处理。从代码可以看出,它主要是向context中注册了几个类,包括BeanNameGenerator,ConversionService,同时设置了resourceLoader属性以及resourceLoader的classLoader。接下来看applyInitializers,这个函数是把根据之前加载的initializers对context做了初始化。listeners.contextPrepared(context)方法是发送了一个ApplicationContextInitializedEvent事件,这个事件发送的时机是ApplicationContext被创建且Prepared,但是在sources(配置文件)加载之前。接下来是根据source加载bean的定义,主要逻辑放在load(context, source.toArray(new Object[0]))方法里边,这个方法初始化了一个BeanDefinitionLLoader对象,然后根据source的值,load bean对象到BeanDefinitionRegistry中。load方法执行完之后,会发送一个ApplicationPreparedEvent的事件。

       prepareContext方法执行完之后,就该执行refreshContext方法了,这个方法是最重要的一个方法,我会随后单独分析这个方法的逻辑,先把run方法的逻辑走完,在refreshContext之后,是afterRefresh,这是一个空方法,执行完之后,context的初始化就算是完成了。这个时候会发布一个ApplicationStartedEvent事件,然后是执行Runner方法。

发布了30 篇原创文章 · 获赞 4 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/u013015681/article/details/104387927