Spring Boot 基于源码的启动流程详解

目录

SpringBootApplication注解

SpringApplication.run(XXXApplication.class, args)

3:SpringBoot的ConfigurableEnvironment初始化

5:SpringBoot的prepareContext()准备IOC容器数据

6:refreshContext()


SpringBoot在日常工作中带来了很多便利,通过application.properties即可替换默认属性值通过@SpringBootApplication和SpringApplication.run()即可完成Spring容器的启动,并自动为我们创建自动配置的bean,如下图,本文主要关注springboot启动时,自动创建过滤自动配置类、业务bean、加载资源文件的整体流程。

SpringBootApplication注解

@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 {//省略}

可以看到SpringBootApplication实际上是一个组合注解,@Target(ElementType.TYPE) 、@Retention(RetentionPolicy.RUNTIME)、@Documented、@Inherited是java的一个元注解,@Target标志当前注解的使用类型是:类;枚举;接口,还是方法级、属性 等,@Retention是标志者当前注解的生效时期:运行期还是编译时。@Documented标志表明要记录的javadoc注释的类型和类似工具,@Inherited标志着当前注解是支持继承的。

@ComponentScan:扫描某个包下所有标志着的bean组件(Controller、Service、Component、Configuration等),excludeFilters:排除一些过滤器的实现,这个注解在后续启动流程中,会扫描SampleTomcatApplication同目录等级下的所有bean组件,但是并没有配置basePackages,猜想一:应该是后续根据run()中传入的java类,根据类的路径来扫描同目录下所有组件(后续说道)。

@SpringBootConfiguration:也是一个组件注解,没什么可看的,主要是使用@Configuration标志着当前注解有同样配置类注解的效果,使用该注解的类是配置类也会被加载到IOC容器中。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {// 省略}
@EnableAutoConfiguration:也是一个组合注解。java元注解忽略。主要是:
@AutoConfigurationPackage:注解的作用是将 添加该注解的类所在的package 作为 自动配置package 进行管理,目的之一 就是在扫描同package下所有bean组件时,提供目标package。
@Import(AutoConfigurationImportSelector.class):将AutoConfigurationImportSelector类注入到Spring容器中,这个类在后续springboot启动时,通过SpringBoot的SPI机制加载并过滤自动配置类起到关键作用。

SpringApplication.run(XXXApplication.class, args)

上面注解说了下用途,具体代码执行逻辑,下面根据源码,一起来看下:

run()方法点进去,会调用下面这个方法,先是创建SpringApplication,然后执行该类的run()方法。

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

一个个来看:创建SpringbootApplication时,初始化类属性:设置primarySources为启动类class、设置web应用的类型:通常是servlet类型、根据SpringBoot的SPI机制初始化bootstrapRegistryInitializers、initializers、listeners;initializers、listeners主要是Springboot在初始化、启动流程中,初始化属性、发布springBoot的事件监听的作用。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		// 初始化 initializers 属性
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 初始化 listeners 属性
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

关于SpringBoot的SPI机制 简单提下,有兴趣的朋友自行查阅资料深入。

SpringBoot启动时,会加载项目中依赖的jar包下resources下的META-INF的spring.factories中的属性,文件中通常是配置AutoConfiguration、Listeners、Initializers信息,通过SpringFactoriesLoader可以加载,并在特定流程中初始化bean,像springboot的初始化、监听器实现就是在创建SpringApplication类时就初始化了。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// 获取beantype的所有bean name,并创建bean实例。
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

// 加载出spring,factories中的配置类,根据type为key返回一个Map<String, List<String>>,并
// 记录到cache缓存中,getOrDefault()根据type去获取对应的List<String>。type如:
// EnableAutoConfiguration、ApplicationListener等等。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}

下面看下创建好SpringApplication对象后,run()方法的后续流程 。

主要的流程是:

1、应用启动计时、配置headless 属性

2、获取springboot的事件监听,发送starting事件

3、创建springboot应用的environment,此时就会生成application.properties的propertySource

4、打印springboot的banner、 根据webApplicationType去创建IOC容器的类型

5、准备context上下文,初始化、load数据,发送相应事件监听。

6、通过refreshContext(),来加载springIOC的所有组件(spring的核心流程代码,本文不做过多解读)

7、IOC加载完成的后续操作,如:输出启动时长、发送启动成功事件、调用回调方法等整体流程就结束了。

public ConfigurableApplicationContext run(String... args) {
		long startTime = System.nanoTime();
		// 创建DefaultBootstrapContext,将
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		// <2> 配置 headless 属性
		configureHeadlessProperty();
		// 获得 SpringApplicationRunListener 的数组,并启动监听(默认springboot提供了EventPublishingRunListener)
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 创建应用的environment,ConfigDataEnvironmentPostProcessor解析xml文件,生成propertySource,用于后续构建自动注入properties文件的属性填充
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();// 根据webApplicationType去创建IOC容器的类型
			context.setApplicationStartup(this.applicationStartup);
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
			}
			listeners.started(context, timeTakenToStartup);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
			listeners.ready(context, timeTakenToReady);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

下面主要针对3、5、6步骤 进行Springboot的较详细的分析:

3:SpringBoot的ConfigurableEnvironment初始化

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		// Create and configure the environment 根据webApplicationType返回相应默认的environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		// 监听构造environment的数据(application.xml),(通知 SpringApplicationRunListener 的数组,环境变量已经准备完成)
		listeners.environmentPrepared(bootstrapContext, environment);
		DefaultPropertiesPropertySource.moveToEnd(environment);
		Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
				"Environment prefix cannot be set via properties.");
		// 绑定 environment 到 SpringApplication 上
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = convertEnvironment(environment);
		}
		// 如果有 attach 到 environment 上的 MutablePropertySources ,则添加到 environment 的 PropertySource 中。
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

初始化environment时,listeners.environmentPrepared(bootstrapContext, environment);主要是对application.properties配置加载,发送 环境准备完成 事件后,        

EnvironmentPostProcessorApplicationListener监听该事件,并进行处理,遍历所有的EnvironmentPostProcessor执行postProcessEnvironment来填充environment信息。

 其中ConfigDataEnvironmentPostProcessor流程中createConfigDataLocationResolvers()并调用processAndApply()时,

通过SPI机制,load出处理properties、yml的class信息,作为ConfigDataEnvironment一个属性

,便于后续解析文件中的属性

 processAndApply()方法在执行流程中,会通过ConfigDataImporter遍历出resource中的数据最终通过PropertiesPropertySourceLoader来解析出具体配置信息,见下图:

5:SpringBoot的prepareContext()准备IOC容器数据

 主要是设置上下文的环境信息,调用ApplicationContextInitializer来初始化数据,发送contextPrepared监听,加载启动类注册到BeanDefinitionMap,发送contextLoaded监听。

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);// 设置springboot有关的context的属性
		postProcessApplicationContext(context);
		applyInitializers(context);// 部分数据初始化的操作,比如 contextId的 生成(ContextIdApplicationContextInitializer)
		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);//启动IOC容器前发送监听
	}
applyInitializers(context):主要功能:数据初始化或将类注册监听(例:ContextIdApplicationContextInitializer、ServerPortInfoApplicationContextInitializer、DelegatingApplicationContextInitializer)
// DelegatingApplicationContextInitializer
// 从环境信息中获取所有配置的需初始化bean,实例化,并调用其initialize方法
public void initialize(ConfigurableApplicationContext context) {
		ConfigurableEnvironment environment = context.getEnvironment();
		List<Class<?>> initializerClasses = getInitializerClasses(environment);
		if (!initializerClasses.isEmpty()) {
			applyInitializerClasses(context, initializerClasses);
		}
	}

// ContextIdApplicationContextInitializer
// SpringIterableConfigurationPropertySource的getConfigurationProperty()从配置文件中读取spring.application.name属性,未设置则默认application,写死的
	public void initialize(ConfigurableApplicationContext applicationContext) {
		ContextId contextId = getContextId(applicationContext);
		applicationContext.setId(contextId.getId());
		applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId);
	}

//ServerPortInfoApplicationContextInitializer
// 将当前类注册到IOC的监听器中,当有事件发布时,刷新server.port属性
public void initialize(ConfigurableApplicationContext applicationContext) {
		applicationContext.addApplicationListener(this);
	}

6:refreshContext()

在该方法的invokeBeanFactoryPostProcessors(beanFactory)中,通过ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry()来注册bean实例。后续通过AutoConfigurationImportSelector(@import注解导入的类)来扫描过滤自动配置类。

/**
	 * 导入自动配置的核心方法(此处主要是操作自动配置的bean,非业务代码bean)
	 *
	 * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata}
	 * of the importing {@link Configuration @Configuration} class.
	 * @param annotationMetadata the annotation metadata of the configuration class
	 * @return the auto-configurations that should be imported
	 */
	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		// <1> 判断是否开启。如未开启,返回空数组
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		// <2> 获得注解的属性
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		// <3> 获得符合条件的配置类的数组(SPI机制获取所有的EnableAutoConfiguration)
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		// <3.1> 移除重复的配置类
		configurations = removeDuplicates(configurations);
		// <4> 获得需要排除的配置类
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		// <4.1> 校验排除的配置类是否合法
		checkExcludedClasses(configurations, exclusions);
		// <4.2> 从 configurations 中,移除需要排除的配置类
		configurations.removeAll(exclusions);
		// <5> 根据条件(Condition),过滤掉不符合条件的配置类(自动配置bean上的条件)
		configurations = getConfigurationClassFilter().filter(configurations);
		// <6> 触发自动配置类引入完成的事件
		fireAutoConfigurationImportEvents(configurations, exclusions);
		// <7> 创建 AutoConfigurationEntry 对象
		return new AutoConfigurationEntry(configurations, exclusions);
	}

通过SPI机制、反射来创建出OnBeanCondition、OnWebApplicationCondition、OnClassCondition,并作为属性来创建ConfigurationClassFilter,同时也会load出自动配置类及注解上增加的条件注解(例子:@ConditionalOnClass、@ConditionalOnBean等)。后续遍历ConfigurationClassFilter的这个属性,根据这三个filter来过滤出不符合的自动配置类,从加载出当前项目所需要的自动配置类。

// AutoConfigurationImportSelector
List<String> filter(List<String> configurations) {
			long startTime = System.nanoTime();
			String[] candidates = StringUtils.toStringArray(configurations);
			boolean skipped = false;//通过class、webapplication、bean的配置,进行过滤自动加载的配置类
			for (AutoConfigurationImportFilter filter : this.filters) {
				// 模板方法实现,子类具体实现各自的match操作
				boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
				for (int i = 0; i < match.length; i++) {
					if (!match[i]) {
						candidates[i] = null;
						skipped = true;
					}
				}
			}
			if (!skipped) {
				return configurations;
			}
			List<String> result = new ArrayList<>(candidates.length);
			for (String candidate : candidates) {
				if (candidate != null) {
					result.add(candidate);
				}
			}
			if (logger.isTraceEnabled()) {
				int numberFiltered = configurations.size() - result.size();
				logger.trace("Filtered " + numberFiltered + " auto configuration class in "
						+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
			}
			return result;
		}

// FilteringSpringBootCondition父类定义通用代码,子类实现(getOutcomes()、getMatchOutcome())
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
		ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
		ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
		boolean[] match = new boolean[outcomes.length];
		for (int i = 0; i < outcomes.length; i++) {
			match[i] = (outcomes[i] == null || outcomes[i].isMatch());
			if (!match[i] && outcomes[i] != null) {
				logOutcome(autoConfigurationClasses[i], outcomes[i]);
				if (report != null) {
					report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
				}
			}
		}
		return match;
	}
//OnClassCondition的实现,其他两种请见源码
private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end,
				AutoConfigurationMetadata autoConfigurationMetadata) {
			ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
			for (int i = start; i < end; i++) {
				String autoConfigurationClass = autoConfigurationClasses[i];
				if (autoConfigurationClass != null) {
					String candidates = autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass");
					if (candidates != null) {
						outcomes[i - start] = getOutcome(candidates);
					}
				}
			}
			return outcomes;
		}
		// 判断配置类ConditionalOnClass上的类,是否存在,不存在就排除该配置类,不进行加载
		private ConditionOutcome getOutcome(String candidates) {
			try {
				if (!candidates.contains(",")) {
					return getOutcome(candidates, this.beanClassLoader);
				}
				for (String candidate : StringUtils.commaDelimitedListToStringArray(candidates)) {
					ConditionOutcome outcome = getOutcome(candidate, this.beanClassLoader);
					if (outcome != null) {
						return outcome;
					}
				}
			}
			catch (Exception ex) {
				// We'll get another chance later
			}
			return null;
		}

		private ConditionOutcome getOutcome(String className, ClassLoader classLoader) {
			if (ClassNameFilter.MISSING.matches(className, classLoader)) {
				return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
						.didNotFind("required class").items(Style.QUOTE, className));
			}
			return null;
		}

通过下图可以看到,过滤完成后,在reader.loadBeanDefinitions(configClasses)时,会根据bean组件上的条件注解,在过滤一遍,这里自动配置和业务bean都会在check一下。

 ComponentScan注册包下所有的bean:ConfigurationClassParser的doProcessConfigurationClass()中,根据 class信息的包名来扫描bean组件,由此也验证了猜想一猜对了:

Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旺仔丷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值