SpringBoot启动流程简单分析

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

SpringBoot是基于Spring框架的,只是简化了Spring 项目的搭建,SpringBoot项目启动非常简单调用SpringApplication的run方法就可以启动一个SpringBoot项目
在这里插入图片描述

接下来我们来看下SpringBoot的启动流程


一、SpringApplication

  1. SpringApplication.run(DemoApplication.class, args)先调用静态方法创建一个SpringApplication对象
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}
  1. 构造方法创建SpringApplication给一些关键属性赋值
	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.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

第4行是run方法传入的类,注意这个类上有@SpringBootApplication注解
第5行设置应用的类型有 NONE SERVLET REACTIVE三种
第6行读取spring.factories文件的Bootstrapper接口实现类设置到SpringApplication容器
第7行读取spring.factories文件的ApplicationContextInitializer接口实现类设置到SpringApplication容器
第8行读取spring.factories文件的ApplicationListener接口实现类设置到SpringApplication容器

二、SpringApplication run方法

public ConfigurableApplicationContext run(String... args) {
		
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			configureIgnoreBeanInfo(environment);
		
			Banner printedBanner = printBanner(environment);
			
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
			
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class<?>[] { ConfigurableApplicationContext.class }, context);
			prepareContext(bootstrapContext, 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);
			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;
	}
  1. 第3行 创建一个StopWatch 对象,这个是Spring的一个工具类,可方便的对程序部分代码进行ms级别的计时
  2. 第5行 创建一个DefaultBootstrapContext 对象(这个类里面提供了一些注册方法),我们管它叫SpringBoot容器类吧,并初始化Bootstrapper实现类
private DefaultBootstrapContext createBootstrapContext() {
		DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
		this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
		return bootstrapContext;
	}
  1. 第7行创建一个SpringBootExceptionReporter集合,后面会读取spring.factories配置文件 SpringBootExceptionReporter 实现类
  2. 第8行设置java.awt.headless系统变量
  3. 第10-11行 拿到spring.factories配置文件 SpringApplicationRunListener 实现类(SpringBoot中只有EventPublishingRunListener一个实现类,这个类主要在SpringBoot启动过程中发布事件) 并且启动,发布ApplicationStartingEvent事件,EventPublishingRunListener创建过程中,拿到了SpringApplication 中的监听器,也就是这里发布的事件只有spring.factories文件配置的ApplicationListener接口实现类才能监听到
	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
  1. 第13创建 ApplicationArguments (应用程序启动参数)
  2. 第15-16行准备环境配置信息ConfigurableEnvironment,同时发布ApplicationEnvironmentPreparedEvent事件, EnvironmentPostProcessorApplicationListener监听器在监听到ApplicationEnvironmentPreparedEvent事件后,会调用EnvironmentPostProcessor接口的postProcessEnvironment,来处理环境信息,在其它的SpringCloud组件中的spring.factories也有定义EnvironmentPostProcessor扩展
  3. 第18行打印启动日志
  4. 第20-21行创建一个Spring容器
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);
		}
	};

如果是Servlet环境就是AnnotationConfigServletWebServerApplicationContext

在这里插入图片描述
这个类内部定义了 AnnotatedBeanDefinitionReader ClassPathBeanDefinitionScanner两个解析器,都是用来注册Bean到容器的

  1. 获取spring.factories配置文件 SpringBootExceptionReporter 实现类,前面创建的Spring容器context作为参数
  2. 第25行prepareContext我们具体来看看源码
	private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		//前面准备好的环境信息设置添加到创建好的Spring容器
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		//调用spring.factories 配置的ApplicationContextInitializer  initialize方法,入参是Spring容器
		applyInitializers(context);
		// 发布ApplicationContextInitializedEvent事件,spring.factories文件配置`的ApplicationListener`接口实现类才能监听到
		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 DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources  对于 SpringApplication.run(DemoApplication.class, args)  这里source就是DemoApplication,注意这个类上有@SpringBootApplication注解
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 加载bean到容器
		load(context, sources.toArray(new Object[0]));
		// 发布ApplicationPreparedEvent事件
		listeners.contextLoaded(context);
	}

  1. 第26行refreshContext刷新下Spring容器,上一步完成了bean的注册,这里再刷新容器
  2. 第27行afterRefresh定义了个钩子
  3. 第32行 listeners.started(context) 发布ApplicationStartedEvent事件

SpringBootApplication注解

SpringBoot项目启动类上都会加上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 

这里有三个注解 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
其中@SpringBootConfiguration注解又有@Configuration注解信息,

前面调用SpringApplication run方法创建AnnotationConfigServletWebServerApplicationContext容器时,向Spring容器添加了一个AnnotatedBeanDefinitionReader ,这里添加了ConfigurationClassPostProcessor后处理器,然后再容器refresh过程中就会处理@SpringBootConfiguration修饰的主类信息了

1 ComponentScan注解

这个主要是指定扫描路径,spring会把指定路径下带有指定注解的类注册到IOC容器中,basePackages、value:指定扫描路径,如果为空则以@ComponentScan注解的类所在的包为基本的扫描路径

2 EnableAutoConfiguration

自动导入接口,可以看到这里注解通过Import注解又导入了个AutoConfigurationImportSelector类,同时又有个@AutoConfigurationPackage注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration

AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage

这个注解导入了一个Registrar类 实现了ImportBeanDefinitionRegistrar接口, ConfigurationClassPostProcessor对于Import的类如果实现这个接口,调用registerBeanDefinitions方法,这里往容器中注册了一个Bean beanClass 是BasePackages

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
		}

		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImports(metadata));
		}

	}

AutoConfigurationImportSelector

我们知道EnableAutoConfiguration 导入的这个类能够可以扫描spring.factories文件中的EnableAutoConfiguration接口实现类,从而将复合条件的类注册到Spring容器
在这里插入图片描述

具体是怎么做到呢,这个需要了解下ConfigurationClassPostProcessor怎么处理Import导入的类,AutoConfigurationImportSelector的继承体系同时实现了ImportSelector DeferredImportSelector两个接口

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

AutoConfigurationImportSelector 实现了DeferredImportSelector接口,ConfigurationClassPostProcessor在处理这个类时调用到了ConfigurationClassParser内部类DeferredImportSelectorHandler 的process方法

public void process() {
			List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
			this.deferredImportSelectors = null;
			try {
				if (deferredImports != null) {
					DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
					deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
					deferredImports.forEach(handler::register);
					handler.processGroupImports();
				}
			}
			finally {
				this.deferredImportSelectors = new ArrayList<>();
			}
		}
// DeferredImportSelectorGroupingHandler
public void register(DeferredImportSelectorHolder deferredImport) {
			Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
			DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
					(group != null ? group : deferredImport),
					key -> new DeferredImportSelectorGrouping(createGroup(group)));
			grouping.add(deferredImport);
			this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
					deferredImport.getConfigurationClass());
		}
// DeferredImportSelectorGroupingHandler
public void processGroupImports() {
			for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
				Predicate<String> exclusionFilter = grouping.getCandidateFilter();
				/**
				 *ImportSelector 接口类selectImports获取的值包装成成一个Entry 集合
				 */
				grouping.getImports().forEach(entry -> {
					ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
					try {
						processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
								Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
								exclusionFilter, false);
					}
					catch (BeanDefinitionStoreException ex) {
						throw ex;
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to process import candidates for configuration class [" +
										configurationClass.getMetadata().getClassName() + "]", ex);
					}
				});
			}
		}
public Iterable<Group.Entry> getImports() {
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			return this.group.selectImports();
		}

先register 再调用processGroupImports,上面的register方法拿到了一个Group对象,这在AutoConfigurationImportSelector就是AutoConfigurationGroup

public Class<? extends Group> getImportGroup() {
		return AutoConfigurationGroup.class;
	}

processGroupImports 方法调用 grouping.getImports()返回的是一个存有Group.Entry对象的迭代器,交由具体的group(AutoConfigurationGroup)去执行process selectImports 方法

   // AutoConfigurationGroup
	public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
			Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
					() -> String.format("Only %s implementations are supported, got %s",
							AutoConfigurationImportSelector.class.getSimpleName(),
							deferredImportSelector.getClass().getName()));
			//getAutoConfigurationEntry 这个方法获取到了spring.factories的 EnableAutoConfiguration 实现类
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(annotationMetadata);
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
		}
// AutoConfigurationGroup
public Iterable<Entry> selectImports() {
			if (this.autoConfigurationEntries.isEmpty()) {
				return Collections.emptyList();
			}
			Set<String> allExclusions = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
			Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
					.collect(Collectors.toCollection(LinkedHashSet::new));
			processedConfigurations.removeAll(allExclusions);

			return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
					.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
					.collect(Collectors.toList());
		}
	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		configurations = removeDuplicates(configurations);
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}

selectImports 就收集EnableAutoConfiguration 实现类包装成一个Entry对象交给Spring 的ConfigurationClassParser 注册到容器当中

总结

这里简单整理了下SpringBoot的启动过程,主要了解SpringApplication run方法怎样创建Spring容器,还有就是AutoConfigurationImportSelector自动注入Bean的原理,这一点需要结合Spring中的ConfigurationClassPostProcessor(处理Configuration注解)容器后处理器来看

另外发现一个奇怪的地方
AutoConfigurationImportSelector 中有一个直接实现ImportSelector接口的selectImports在SpringBoot项目启动过程中实际是没有调用到,这个不知道什么原因
在这里插入图片描述
在这里插入图片描述
AutoConfigurationImportSelector 入口方法是getImportGroup

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值