SpringBoot 2.5.x 启动源码分析

439 阅读6分钟

1.概览

我们在创建一个springBoot项目的时候,默认给了一个启动类DemoApplication

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

其中组合注解@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 {
...
}

这个注解上几种注解的含义如下

  1. @Target (ElementType.TYPE):元注解之一,表明该注解是用在类、接口、枚举类上的。
  2. @Retention(RetentionPolicy.RUNTIME):元注解之一,表示该注解是运行时注解,在项目运行时也保留了这个注解。
  3. @Documented:元注解之一,表示该类在生成帮助文档时,这个注解会被保留。
  4. @Inherited:元注解之一,标识这个注解是可以被子类继承的。
  5. @SpringBootConfiguration:组合注解,效果等同于@Configuration,声明该类是个配置类。
  6. @EnableAutoConfiguration:组合注解,提供自动将带有@Configuration注解配置的bean都初始化到IOC容器中。
  7. @ComponentScan: 组合注解,由于声明哪些包中的类需要被扫描到,excludeFilters 参数是排除掉过滤器匹配到的类。

启动类不加这个注解@SpringBootApplication,一般的springBoot项目也可以run成功,但是扫描不到其他包下的Bean。如果配置了Spring-Web。他会报找不到ServletWebServerFactory这个类,就是因为没有自动扫描依赖包的原因。


我们按照springboot-Web项目启动运行的加载顺序去理清整套流程。 在DemoApplication 类的main方法中调用了静态方法run,该方法就是创建了一个SpringApplication实例并运行run方法。

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

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

SpringApplication的构造参数如下:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   // 资源加载器,默认为null
   this.resourceLoader = resourceLoader;
   Assert.notNull(primarySources, "PrimarySources must not be null");
   // 这地方primarySources就是我们的启动类
   this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
   // 根据classpath中的存在的类推断应用类型(REACTIVE、SERVLET、NONE)
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   // 从Spring.Factories文件中加载bootstrapRegistryInitializer
   this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();  
   // 从Spring.Factories文件中加载ApplicationContextInitializer集合      
   setInitializers((Collection)getSpringFactoriesInstances(ApplicationContextInitializer.class));
   // 从Spring.Factories文件中加载ApplicationListener集合 
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   //推断主类
   this.mainApplicationClass = deduceMainApplicationClass();
}

run方法如下:

public ConfigurableApplicationContext run(String... args) {
                // 1.创建一个计时器记录启动耗时
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
                // 3.创建引导上下文
		DefaultBootstrapContext bootstrapContext = createBootstrapContext();
		// 定义一个可配置的应用上下文对象,ConfigurableApplicationContext下实现了所有类型的应用上下文
		ConfigurableApplicationContext context = null;
		// 设置java.awt.headless值,为true时表明缺少显示屏、键盘或者鼠标
		configureHeadlessProperty();
		//4. 获取运行时监听器集合
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 启动监听器
		listeners.starting(bootstrapContext, this.mainApplicationClass);
		try {
			// 获取启动时的应用参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 5.准备配置环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
			// 配置spring.beaninfo.ignore值
			configureIgnoreBeanInfo(environment);
			// 6.打印启动横幅
			Banner printedBanner = printBanner(environment);
			// 7.根据项目创建上下文
			context = createApplicationContext();
			context.setApplicationStartup(this.applicationStartup);
			// 8.准备上下文
			prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
			// 9.刷新上下文,项目启动完成
			refreshContext(context);
			// 10.刷新后的后置处理
			afterRefresh(context, applicationArguments);
			// 耗时打印
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			// 监听器监听上下文启动完成
			listeners.started(context);
			// 调用实现ApplicationRunner或CommandLineRunner的一些自定义操作,默认没有
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
                      ...
		}
		try {
			// 监听器监听上下文运行
			listeners.running(context);
		}
		catch (Throwable ex) {
                      ...
		}
		return context;
	}

接下来,我们一步步的看run中涉及到的点:

2.创建计时器:StopWatch

StopWatch是org.springframework.util 带的一个计时器,一般会又来记录时间。具体用法可以参考:goodlymoon.com/archives/11…

3.创建引导上下文:DefaultBootstrapContext

该部分会生成一个DefaultBootstrapContext 对象,他实现了接口BootstrapRegistry和BootstrapContext,这个接口是用于注册一些用来共享或者创建成本较大的对象实例。保证这些实例在应用上下文创建完成之前可以使用。

public class DefaultBootstrapContext implements ConfigurableBootstrapContext {
	
	// InstanceSupplier接口的map,InstanceSupplier是个函数式接口,可以生产对应泛型的对象数据
	private final Map<Class<?>, InstanceSupplier<?>> instanceSuppliers = new HashMap<>();

	// 具体实例对象的map,当InstanceSupplier对象对象属性是SCOPE时,将InstanceSupplier接口的对象放入该Map
	private final Map<Class<?>, Object> instances = new HashMap<>();
	
	// 事件广播器
	private final ApplicationEventMulticaster events = new SimpleApplicationEventMulticaster();

	// 注册函数接口
	@Override
	public <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier) {
		register(type, instanceSupplier, true);
	}
	
	// 注册函数接口
	@Override
	public <T> void registerIfAbsent(Class<T> type, InstanceSupplier<T> instanceSupplier) {
		register(type, instanceSupplier, false);
	}
	
	// 注册到instanceSuppliers
	private <T> void register(Class<T> type, InstanceSupplier<T> instanceSupplier, boolean replaceExisting) {
		Assert.notNull(type, "Type must not be null");
		Assert.notNull(instanceSupplier, "InstanceSupplier must not be null");
		synchronized (this.instanceSuppliers) {
			boolean alreadyRegistered = this.instanceSuppliers.containsKey(type);
			if (replaceExisting || !alreadyRegistered) {
				Assert.state(!this.instances.containsKey(type), () -> type.getName() + " has already been created");
				this.instanceSuppliers.put(type, instanceSupplier);
			}
		}
	}
	
	// 判断类是否已经注册到instanceSuppliers
	@Override
	public <T> boolean isRegistered(Class<T> type) {
		synchronized (this.instanceSuppliers) {
			return this.instanceSuppliers.containsKey(type);
		}
	}

	// 获取泛型对象的函数接口
	@Override
	@SuppressWarnings("unchecked")
	public <T> InstanceSupplier<T> getRegisteredInstanceSupplier(Class<T> type) {
		synchronized (this.instanceSuppliers) {
			return (InstanceSupplier<T>) this.instanceSuppliers.get(type);
		}
	}
	
	// 添加BootstrapContextClosedEvent事件监听器
	@Override
	public void addCloseListener(ApplicationListener<BootstrapContextClosedEvent> listener) {
		this.events.addApplicationListener(listener);
	}
	
	// 获取泛型对象
	@Override
	public <T> T get(Class<T> type) throws IllegalStateException {
		return getOrElseThrow(type, () -> new IllegalStateException(type.getName() + " has not been registered"));
	}
	
	// 获取泛型对象
	@Override
	public <T> T getOrElse(Class<T> type, T other) {
		return getOrElseSupply(type, () -> other);
	}
	
	// 获取泛型对象
	@Override
	public <T> T getOrElseSupply(Class<T> type, Supplier<T> other) {
		synchronized (this.instanceSuppliers) {
			InstanceSupplier<?> instanceSupplier = this.instanceSuppliers.get(type);
			return (instanceSupplier != null) ? getInstance(type, instanceSupplier) : other.get();
		}
	}
	
	// 获取泛型对象
	@Override
	public <T, X extends Throwable> T getOrElseThrow(Class<T> type, Supplier<? extends X> exceptionSupplier) throws X {
		synchronized (this.instanceSuppliers) {
			InstanceSupplier<?> instanceSupplier = this.instanceSuppliers.get(type);
			if (instanceSupplier == null) {
				throw exceptionSupplier.get();
			}
			return getInstance(type, instanceSupplier);
		}
	}
	
	// 获取泛型对象
	@SuppressWarnings("unchecked")
	private <T> T getInstance(Class<T> type, InstanceSupplier<?> instanceSupplier) {
		T instance = (T) this.instances.get(type);
		if (instance == null) {
			instance = (T) instanceSupplier.get(this);
			if (instanceSupplier.getScope() == Scope.SINGLETON) {
				this.instances.put(type, instance);
			}
		}
		return instance;
	}

	/**
	 * Method to be called when {@link BootstrapContext} is closed and the
	 * {@link ApplicationContext} is prepared.
	 * @param applicationContext the prepared context
	 */
	 // 该方法会在BootstrapContext上下文关闭,ApplicationContext准备好的时候调用,广播一个BootstrapContextClosedEvent事件
	public void close(ConfigurableApplicationContext applicationContext) {
		this.events.multicastEvent(new BootstrapContextClosedEvent(this, applicationContext));
	}
}

DefaultBootstrapContext 类中有一个对象:applicationEventMulticaster是一个事件广播器接口。这个接口可以管理很多个ApplicationListener对象。并将事件发布给这些监听器。它是一个标准的观察者模式,对于他内部的监听者applicationListeners,每次事件到来都会一一获取通知。

4.获取监听器集合:SpringApplicationRunListeners

在获取监听器之前,它定义了一个ConfigurableApplicationContext 上下文接口(该接口是众多上下文实现类的父接口:可以参考juejin.cn/post/700332… ,里面对IOC容器架构有一个统一的描述)

之后,我们深入研究一下getRunListeners方法:

	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		// 构建一个新的SpringApplicationRunListeners类,对象listeners 从getSpringFactoriesInstances方法中获取SpringApplicationRunListener接口的实现类集合
		return new SpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
	}

	/**
	* 通过spring 的SpringFactories机制,去获取META-INF/spring.factories配置中type类型对应的接口实现类
	**/
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		// 从loadFactoryNames方法获取type对应的实现类名称集合
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

在该方法中通过SpringFactories机制,去获取META-INF/spring.factories配置中type类型对应的接口实现类。通过spring-boot.jar包下META-INF/spring.factories文件可以看到SpringApplicationRunListener.class对应的实现类是org.springframework.boot.context.event.EventPublishingRunListener image.png

最终监听器集合SpringApplicationRunListeners对象获取到的监听器只包含EventPublishingRunListener这一个。

5.配置环境准备:ConfigurableEnvironment

// listeners和bootstrapContext是创建的监听器集合和启动上下文,applicationArguments是应用程序启动参数
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
   // Create and configure the environment
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   ConfigurationPropertySources.attach(environment);
   listeners.environmentPrepared(bootstrapContext, environment);
   DefaultPropertiesPropertySource.moveToEnd(environment);
   Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
         "Environment prefix cannot be set via properties.");
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
            deduceEnvironmentClass());
   }
   ConfigurationPropertySources.attach(environment);
   return environment;
}

// 根据SpringApplication对象构造时获取的webApplicationType选择上下文环境
private ConfigurableEnvironment getOrCreateEnvironment() {
	if (this.environment != null) {
		return this.environment;
	}
	switch (this.webApplicationType) {
	case SERVLET:
		return new ApplicationServletEnvironment();
	case REACTIVE:
		return new ApplicationReactiveWebEnvironment();
	default:
		return new ApplicationEnvironment();
	}
}

在web项目中,返回的ConfigurableEnvironment实现类是ApplicationServletEnvironment。

6.打印启动横幅:Banner

Banner printedBanner = printBanner(environment);

这行代码最终干了这么一件事,我们也可以自己定制他~

temp.jpg

7.创建上下文:ConfigurableApplicationContext

protected ConfigurableApplicationContext createApplicationContext() {
   // 根据type调用工厂类获取实例
   return this.applicationContextFactory.create(this.webApplicationType);
}

ApplicationContextFactory DEFAULT = (webApplicationType) -> {
   try {
      switch (webApplicationType) {
      case SERVLET:
         // 带注解配置的servlet web服务器应用上下文
         return new AnnotationConfigServletWebServerApplicationContext();
      case REACTIVE:
        // 带注解配置的reactive web服务器应用上下文
         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);
   }
};

因为项目加了spring-web包,所以最后返回的是AnnotationConfigServletWebServerApplicationContext对象

8.准备上下文:AnnotationConfigServletWebServerApplicationContext

在方法prepareContext中会对应用上下文进行赋值,所做的工作如下:

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
		ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments, Banner printedBanner) {
	context.setEnvironment(environment);
	// 后置处理器注册(转换器、bean名称生成器、资源加载器)
	postProcessApplicationContext(context);
	// 获取ApplicationContextInitializer并初始化它们
	applyInitializers(context);
	// 广播ApplicationContextInitializedEvent事件
	listeners.contextPrepared(context);
	// 关闭启动上下文
	bootstrapContext.close(context);
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}
	// 将ApplicationArguments和Banner注册到ioc容器工厂
	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());
	}
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	// 加载BeanDefinitionLoader
	load(context, sources.toArray(new Object[0]));
	// 广播ApplicationPreparedEvent事件
	listeners.contextLoaded(context);
}

9.刷新上下文:AnnotationConfigServletWebServerApplicationContext

private void refreshContext(ConfigurableApplicationContext context) {
   if (this.registerShutdownHook) {
      // 注册应用关闭时的钩子函数,用来清理资源
      shutdownHook.registerApplicationContext(context);
   }
   // 调用父类抽象方法
   refresh(context);
}

此处refresh()方法调用的是抽象类AbstractApplicationContext的refresh方法,接下来将涉及到spring的IOC容器的初始化和依赖注入:

public void refresh() throws BeansException, IllegalStateException {
	// 加锁处理容器刷新
	synchronized (this.startupShutdownMonitor) {
		// 记录步骤
		StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

		// 刷新前的准备工作
		prepareRefresh();

		// 获取一个可重复刷新的Bean工厂
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// 刷新前对Bean工厂的准备工作
		prepareBeanFactory(beanFactory);

		try {
			// bean工厂创建后的后置处理(用于子类拓展)
			postProcessBeanFactory(beanFactory);
			// 记录步骤beanPostProcess
			StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
			// 执行上下文工厂中注册的后置处理器
			invokeBeanFactoryPostProcessors(beanFactory);

			// 注册bean创建的拦截器。
			registerBeanPostProcessors(beanFactory);
		    // 记录步骤关闭
			beanPostProcess.end();

			// 初始化上下文的信息国际化
			initMessageSource();

			// 初始化上下文的事件广播
			initApplicationEventMulticaster();

			// 初始化子类的bean
			onRefresh();

			// 注册监听器
			registerListeners();

			// 初始化剩余的单例bean
			finishBeanFactoryInitialization(beanFactory);

			// 刷新完成时处理(广播世界等)
			finishRefresh();
		}
		catch (BeansException ex) {
			...
		}
		finally {
			// 重置缓存
			resetCommonCaches();
			// 记录步骤关闭
			contextRefresh.end();
		}
	}
}

10.刷新后的后置处理

afterRefresh(context, applicationArguments)

这是个空方法,留给开发者拓展使用