源码
版本为:3.3.x
引言
首先,需要先前往Spring Boot的github地址clone对应的源码;然后配置好自己的Gradle就好了。
Spring Boot的Demo代码主要存放在spring-boot-tests/spring-boot-smoke-tests下,我们从spring-boot-smoke-tests-tomcat为例开始调试阅读。
内容较多,可以只读自己感兴趣的章节
主函数
@SpringBootApplication
public class SampleTomcatApplication {
private static final Log logger = LogFactory.getLog(SampleTomcatApplication.class);
@Bean
protected ServletContextListener listener() {
return new ServletContextListener() {
@Override
public void contextInitialized(ServletContextEvent sce) {
logger.info("ServletContext initialized");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
logger.info("ServletContext destroyed");
}
};
}
public static void main(String[] args) {
// 在SpringBoot程序中所有的开始都是.run()
SpringApplication.run(SampleTomcatApplication.class, args);
}
}
SpringBoot程序中,一切的开始都来自于xxxxxx#main中的SpringApplication.run(Class<?>, String[]);函数
new SpringApplicaiton(Class<?>[])
该方法首先创建一个SpringApplicaiton对象
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 由SpringBootApplication.run()调用时, resourceLoader为null, primarySources为主类
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 获得web应用类型
this.properties.setWebApplicationType(WebApplicationType.deduceFromClasspath());
// 从META-INF/spring.factories中加载BootstrapRegistryInitializer实现类
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 加载META-INF/spring.factories中的ApplicationContextInitializer实现类
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 加载META-INF/spring.factories中的ApplicationListener实现类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 寻找main方法所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
- resourceLoader: 由SpringBootApplication.run()调用时, resourceLoader为null
- primarySources: 顾名思义,就是主类
- properties.setWebApplicationType(): 用来记录当前的应用类型,不同的应用类型会执行不同的操作
- bootstrapRegistryInitializers: 加载所有BootstrapContext的初始化器
- setInitializers():加载所有的ApplicationContext初始化器
- setListeners():加载所有的ApplicaitonListener
- mainApplicationClass:用于记录主类(通过main()方法寻找)
// WebApplicationType.deduceFromClasspath()
public enum WebApplicationType {
/**
* 非web应用,不启动内嵌web服务器
*/
NONE,
/**
* servlet web应用,启动内嵌servlet web服务器
*/
SERVLET,
/**
* reactive web应用,启动内嵌reactive web服务器
*/
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = { "jakarta.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
/**
* 基于classpath检测应用类型 {@link WebApplicationType}
*/
static WebApplicationType deduceFromClasspath() {
// 当存在webflux相关类,不存在webmvc和jersey相关类时,返回reactive
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 当不存在servlet相关类时,返回非web应用
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 保底返回servlet应用
return WebApplicationType.SERVLET;
}
// ......
}
SpringBoot就是根据classpath下的类来判断应用类型的;当然也可以通过
spring.main.web-application-type配置,此项配置会在prepareEnvironment阶段被应用
.run(String... )
在run方法中,最主要的两个语句是prepareContext()与refreshContext();当然其他代码的作用,这里也将会讲解。
/**
* 运行SpringApplication 创建并刷新一个新的{@link ApplicationContext}.
* @param args 应用程序参数(通常从Java main方法传递)
* @return 运行中的{@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
// startup 本质上是一个计时器,当前生成的类为StandardStartup
Startup startup = Startup.create();
// 注册关闭钩子
if (this.properties.isRegisterShutdownHook()) {
SpringApplication.shutdownHook.enableShutdownHookAddition();
}
// 创建BootstrapContext
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 配置是否有UI
configureHeadlessProperty();
// 创建RunListeners,参数最终用于`this.constructor.newInstance(args);`
// 运行监听器主要是用于监听SpringApplication的运行过程
SpringApplicationRunListeners listeners = getRunListeners(args);
// 触发启动中事件
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 将args封装参数applicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// Banner就是启动时打印的logo,返回 打印完对象的banner
// 优先级:spring.banner.location > banner.txt > fallbackBanner > defaultBanner
Banner printedBanner = printBanner(environment);
// applicationContextFactory根据应用类型创建不同的ApplicationContext
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup); // 度量各个阶段的性能
// 预处理上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新上下文
refreshContext(context);
// 刷新后处理
afterRefresh(context, applicationArguments);
startup.started();
if (this.properties.isLogStartupInfo()) {
new StartupInfoLogger(this.mainApplicationClass, environment).logStarted(getApplicationLog(), startup);
}
// 触发启动完成事件
listeners.started(context, startup.timeTakenToStarted());
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, listeners);
}
try {
if (context.isRunning()) {
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
throw handleRunFailure(context, ex, null);
}
return context;
}
Startup
/** * 这是新版写法 */ Startup startup = Startup.create(); // statement startup.started(); // statement listeners.started(context, startup.timeTakenToStarted()); /** * 这是旧版写法 */ long startTime = System.nanoTime(); // statement Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime); // statement listeners.started(context, timeTakenToStartup);Startup 本质上是拿来计时的;用来计算从run开始到applicationContext构建完成所需的时间
BootstrapContext
在run()方法执行之初,ApplicationContext构建之前,SpringBoot会先去构建BootstrapContext
BootstrapContext是一个简单的引导上下文,其在启动以及 Environment 后处理期间可用,直到ApplicationContext可用为止。可以提供对单例的惰性访问,这些单例可能创建成本很高;或者在ApplicationContext可用之前,需要共享一些单例
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
// statement
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// statement
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// statement
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// statement
}
/**
* 创建BootstrapContext,并使用所有的BootstrapRegistryInitializer进行初始化
*/
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrapRegistryInitializers
.forEach((initializer) ->
initializer.initialize(bootstrapContext));
return bootstrapContext;
}
上面的代码十分简单,new一个BootstrapContext,然后使用initializers进行初始化;在此我们可以自己定义一些初始化类进行初始化。
// 触发所有SpringApplicationRunListener#starting()
listeners.starting(bootstrapContext, this.mainApplicationClass);
详细SpringApplicationRunListener结构,见附录
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// statement
listeners.environmentPrepared(bootstrapContext, environment);
// statement
}
在prepareEnvironment内,涉及到bootstrapContext仅有一句,用于触发environmentPrepared阶段
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// statement
// 发布BootstrapContextClosedEvent事件
// 事件参数有bootstrapContext与ApplicationContext
bootstrapContext.close(context);
// statement
}
而在prepareContext内,也仅有一句bootstrapContext.close(context);,该代码会触发 new BootstrapContextClosedEvent(bootstrapContext, applicationContext)事件。
小结
从上述我们可以看出,bootstrapContext在从创建到关闭,与之相关的语句都与listenter相关;
所有我们推测bootstrapContext只是在applicationContext创建前,SpringBoot提供的一个简单容器。
在创建时我们可以实现初始化器自定义该容器,在starting, enironmentPrepared时自定义RunListenter自定义处理或将环境信息添加进入bootstrapContext,在close时,可以将bootstrapContext中的数据转入applicationContext
我们将在下面的SpringApplicationRunListeners章节具体讲解如何使用这个类。
configureHeadlessProperty();
/**
* 根据系统属性设置是否启用headless模式
* headless是应用的一种配置模式。在服务器可能缺少显示设备、键盘、鼠标等外设的情况下使用这种模式。
* 1. 如果应用不需要任何head,那么有无这个配置没有任何影响;
* 2. 如果应用有弹出窗口之类的操作,那么在headless模式下这种操作会被阻止。
* 默认情况下等价于 {@code System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, "true")}
*/
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
想修改,就去自己在启动时修改VM option-Djava.awt.headless=false
SpringApplicationRunListeners
SpringApplicationRunListeners是用来监听run函数执行状态的监听器,涉及到此监听器的有以下几条语句,其中bootstrapContext指的是BootstrapContext;context指的是ApplicationContext
public ConfigurableApplicationContext run(String... args) {
// 运行监听器主要是用于监听SpringApplication.run()的运行阶段
SpringApplicationRunListeners listeners = getRunListeners(args);
// 触发启动中阶段
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// ......
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
{
listeners.environmentPrepared(bootstrapContext, environment);
}
// ......
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
{
// ......
// 监听器触发上下文准备好阶段
listeners.contextPrepared(context);
// ......
// 触发上下文加载完成阶段
listeners.contextLoaded(context);
// ......
}
// ......
// 触发启动完成阶段
listeners.started(context, startup.timeTakenToStarted());
// ......
}
catch (Throwable ex) {
// 触发run失败阶段
throw handleRunFailure(context, ex, listeners);
}
try {
if (context.isRunning()) {
// 触发准备好阶段
listeners.ready(context, startup.ready());
}
}
catch (Throwable ex) {
// 触发run失败阶段
throw handleRunFailure(context, ex, null);
}
return context;
}
其中getRunListeners(args)代码如下
private SpringApplicationRunListeners getRunListeners(String[] args) {
ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
argumentResolver = argumentResolver.and(String[].class, args);
// 从META-INF/spring.factories中加载SpringApplicationRunListener实现类
List<SpringApplicationRunListener> listeners = getSpringFactoriesInstances(SpringApplicationRunListener.class,
argumentResolver);
// 默认为null
SpringApplicationHook hook = applicationHook.get();
SpringApplicationRunListener hookListener = (hook != null) ? hook.getRunListener(this) : null;
if (hookListener != null) {
listeners = new ArrayList<>(listeners);
listeners.add(hookListener);
}
return new SpringApplicationRunListeners(logger, listeners, this.applicationStartup);
}
SpringApplicationRunListener类主要来自于两部分
- 来自于
META-INF/spring.factories下 - 来自于hook.getRunListener(this);默认为null
在默认情况下,仅存在
EventPublishingRunListener,其会包装成事件去触发我们初始化SpringAppicaiton是加载的ApplicationListener类// 加载META-INF/spring.factories中的ApplicationListener实现类 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
小结
根据以上信息可以得知,我们是无法通过仅仅在本地创建对应实现类来自定义监听的;必须将对应的实现类添加到META-INF/spring.factories下
- 实现自己的SpringApplicaitonRunListener类
package smoketest.tomcat.runListenters;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.SpringApplicationRunListener;
public class MyListener implements SpringApplicationRunListener {
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
System.out.println("smoketest.tomcat.runListenters.MyListener");
}
}
- 在本项目resource下添加spring.factories
当然你也可以利用EventPublishingRunListener去触发ApplicationListener;但要做的事也是一样的。
DefaultApplicationArguments(args);
封装args用的;不细讲
prepareEnvironment(...)
用于初始化应用环境, 还涉及到加载多数据源,将spring.main前缀的配置加载到ApplicationProperties,还涉及到数据源优先级排序。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 根据web应用类型创建不同的Environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 根据args配置Environment
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 监听器触发环境准备好事件
listeners.environmentPrepared(bootstrapContext, environment);
// 将环境中的applicationInfo信息放在环境信息的最后
ApplicationInfoPropertySource.moveToEnd(environment);
// 将环境中的defaultProperties放在环境信息的最后
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
// 将environment中spring.main开头的属性绑定到 ApplicationProperties
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
// 如果有必要, 将environment转换为deduceEnvironmentClass()类型
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
// 将configurationProperties绑定到environment, 放在environment的最前面
ConfigurationPropertySources.attach(environment);
return environment;
}
一共有以下几种数据源
咱们最常用的application.properties在第6个,为OriginTrackedMapPropertySource,读属性时,使用迭代器模式,依次搜索,直到找到对应属性或者结束
printBanner(environment)
打印logo,springboot在启动时可以去打印对应的logo;如果大家有自定义logo或删除logo的需求,可以看这边
private Banner printBanner(ConfigurableEnvironment environment) {
// 查询是否有关闭logo的设置
if (this.properties.getBannerMode(environment) == Banner.Mode.OFF) {
return null;
}
// 加载器
ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
: new DefaultResourceLoader(null);
// 创建打印类
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
// 选择用logger打印,还是out流打印
if (this.properties.getBannerMode(environment) == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
/**
* 判断是否开启banner
* 如果this.bannerMode不为空,返回this.bannerMode
* 如果开启了structuredLoggingEnabled,返回Mode.OFF
* 否则返回Mode.CONSOLE
*/
Mode getBannerMode(Environment environment) {
if (this.bannerMode != null) {
return this.bannerMode;
}
boolean structuredLoggingEnabled = environment
.containsProperty(LoggingSystemProperty.CONSOLE_STRUCTURED_FORMAT.getApplicationPropertyName());
return (structuredLoggingEnabled) ? Mode.OFF : Banner.Mode.CONSOLE;
}
Mode getBannerMode(Environment environment) {
if (this.bannerMode != null) {
return this.bannerMode;
}
boolean structuredLoggingEnabled = environment
.containsProperty(LoggingSystemProperty.CONSOLE_STRUCTURED_FORMAT.getApplicationPropertyName());
return (structuredLoggingEnabled) ? Mode.OFF : Banner.Mode.CONSOLE;
}
如果允许打印,在bannerPrinter.print内的getBanner设置了banner的优先级顺序
/**
* 如果没有spring.banner.location与banner.txt
* 去加载 fallbackBanner,如果fallbackBanner也没有,就去用默认的
*/
private Banner getBanner(Environment environment) {
Banner textBanner = getTextBanner(environment);
if (textBanner != null) {
return textBanner;
}
if (this.fallbackBanner != null) {
return this.fallbackBanner;
}
return DEFAULT_BANNER; // 加载默认logo
}
/**
* 先去读取environment中spring.banner.location指定的位置
* 如果为空则读取resourceLoader下的banner.txt
* 如果存在banner.txt就去封装返回
* 如果不存在就返回null
*/
private Banner getTextBanner(Environment environment) {
String location = environment.getProperty(BANNER_LOCATION_PROPERTY, DEFAULT_BANNER_LOCATION);
Resource resource = this.resourceLoader.getResource(location);
try { // 默认resource.exists() == false
if (resource.exists() && !resource.getURL().toExternalForm().contains("liquibase-core")) {
return new ResourceBanner(resource);
}
}
catch (IOException ex) {
// Ignore
}
return null;
}
小结
关闭banner: 2选1 根据上面的源码,我们可以得出
- 配置
spring.main.banner-mode=off,在prepareEnvironment阶段,该配置会被写入ApplicationProperties - 配置
logging.structured.format.console=ecs,该配置会将log以json形式输出,导致关闭banner
如果只是单纯关闭banner,只需要选1
自定义banner
根据上面的源码,我们可以得出springboot加载banner的优先级
spring.banner.location > banner.txt > fallbackBanner > defaultBanner
- 配置
spring.banner.location,指定banner位置 - 直接在
resource下创建banner.txt文件
banner字符画在线生成网站:www.bootschool.net/ascii
ApplicationContext
ApplicationContext主要涉及到以下语句,其中最重要的是prepareContext(...)与refreshContext(...)
// 创建上下文
context = createApplicationContext();
// applicationStartup用于检查应用程序的启动阶段
context.setApplicationStartup(this.applicationStartup);
// 预处理上下文,重要功能是将主类注册到BeanFactory中,但未去解析主类
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新上下文,会去解析主类,在这里会将所有的bean加载到context中
// 此时context已经可用
refreshContext(context);
// 刷新后处理
afterRefresh(context, applicationArguments);
// ......
callRunners(context, applicationArguments);
prepareContext(...)
在该方法中,Spring Boot 会对应用程序上下文 (ApplicationContext) 进行一系列的准备工作,确保在应用程序启动之前,上下文已经正确配置并准备好处理请求。这个方法是 Spring Boot 启动流程中的一个重要步骤。其中前部分都只是对context的配置
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 应用上下文设置环境
context.setEnvironment(environment);
// 后处理应用上下文
// 1. 如果beanNameGenerator不为null,则将其注册为单例bean,注册进context
// 2. 如果resourceLoader不为null,则将它的加载器设置为resourceLoader或resourceLoader的加载器
// 3. 如果addConversionService为true,则配置Object转换服务
postProcessApplicationContext(context);
// 将AOT生成的初始化器添加进初始化器列表,默认没必要
addAotGeneratedInitializerIfNecessary(this.initializers);
// 应用初始化器,对context进行初始化
applyInitializers(context);
// 触发发布上下文准备好阶段
listeners.contextPrepared(context);
// 发布BootstrapContextClosedEvent事件,告知已经关闭的bootstrapContext和启用的applicationContext
bootstrapContext.close(context);
if (this.properties.isLogStartupInfo()) {
logStartupInfo(context.getParent() == null);
logStartupInfo(context);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 将Arguments注册为进BeanFactory
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
// 将打印的banner注册进BeanFactory
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 如果beanFactory是AutowiredCapableBeanFactory,则设置是否允许循环引用,
// 可使用spring.main.allow-circular-references配置
if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) {
autowireCapableBeanFactory.setAllowCircularReferences(this.properties.isAllowCircularReferences());
// 如果beanFactory是ListableBeanFactory,则设置是否允许BeanDefinition覆盖
// 可使用spring.main.allow-bean-definition-overriding配置
if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) {
listableBeanFactory.setAllowBeanDefinitionOverriding(this.properties.isAllowBeanDefinitionOverriding());
}
}
// 如果允许lazy初始化,则添加LazyInitializationBeanFactoryPostProcessor
// 可使用spring.main.lazy-initialization配置
if (this.properties.isLazyInitialization()) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 如果允许keepAlive,则添加KeepAlive监听器
// 可使用spring.main.keep-alive配置
if (this.properties.isKeepAlive()) {
context.addApplicationListener(new KeepAlive());
}
// 添加PropertySourceOrderingBeanFactoryPostProcessor
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
// 如果没有使用生成的Artifacts
if (!AotDetector.useGeneratedArtifacts()) {
// Load the sources; 最重要的部分
// 1. 主类一定是source
// 2. 其他的source,来自于spring.main.sources配置
// source值springboot开始扫描的起点
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 将sources集合添加进applicationContext
load(context, sources.toArray(new Object[0]));
}
// 触发上下文加载完成阶段
listeners.contextLoaded(context);
}
下面是 Set<Object> sources = getAllSources();所执行的方法。
/**
* 返回一个不可变的 source 集合,集合内的source将被添加进applicationContext。集合会将构造函数中
* 指定的任何主source与配置的的其他source组合在一起。
*/
public Set<Object> getAllSources() {
Set<Object> allSources = new LinkedHashSet<>();
if (!CollectionUtils.isEmpty(this.primarySources)) {
allSources.addAll(this.primarySources);
}
if (!CollectionUtils.isEmpty(this.properties.getSources())) {
allSources.addAll(this.properties.getSources());
}
return Collections.unmodifiableSet(allSources);
}
默认情况下, sources 中只添加标有@SpringBootApplication的类
/**
* 将beans加载到applicationContext中
* 此时仅将sources加载到context中,还未刷新context
* @param context the context to load beans into
* @param sources the sources to load
*/
protected void load(ApplicationContext context, Object[] sources) {
// ......
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
loader.load();最终会去将bean注册进beanDefinitionMap,其最下层执行的函数如下所示
public void registerBean(Class<?> beanClass) {
doRegisterBean(beanClass, null, null, null, null);
}
/**
* 注册给定的bean,从类的注解上解析其元数据
* @param beanClass bean的类型
* @param name bean的名字,当前为null
* @param qualifiers 类上注解,当前为null
* @param supplier 创建bean的回调,当前为null
* @param customizers one or more callbacks for customizing the factory's
* {@link BeanDefinition}, for example, setting a lazy-init or primary flag, 依然是回调,当前为null
*/
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
// 设置一下属性信息
abd.setAttribute(ConfigurationClassUtils.CANDIDATE_ATTRIBUTE, Boolean.TRUE);
abd.setInstanceSupplier(supplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
// 如果该类上存在注解(当前为null)
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
// 此注解为 Primary
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
// 此注解为 Fallback
else if (Fallback.class == qualifier) {
abd.setFallback(true);
}
// 此注解为 Lazy
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
// 保底操作
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
// 如果存在创建回调
if (customizers != null) {
for (BeanDefinitionCustomizer customizer : customizers) {
customizer.customize(abd);
}
}
// 将元信息与name封装起来
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// registry就是context,在servlet下,其applicationContext实现了对应接口
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
此类中,会解析sources中类,并将信息转换成BeanDefinition,注册进context的beanDefinitionMap
context的beanfactory前后对比
小结
prepareContext核心操作就是把每个source加入context的beanFactory中,作为springboot去扫描类的起点。
refreshContext(...)
SpringBoot的run方法下的refreshContext,该函数会负责刷新ApplicationContext,确保所有 Bean 都被正确初始化并准备好处理请求,其中最主要的是invokeBeanFactoryPostProcessors(...)其会触发自动加载。以及registerBeanPostProcessors(...)。不过这部分比较复杂,同时还涉及到自动加载与配置,准备在下一篇进行补充。
private void refreshContext(ConfigurableApplicationContext context) {
// 如果注册了关闭钩子,则将其注册进context
if (this.properties.isRegisterShutdownHook()) {
shutdownHook.registerApplicationContext(context);
}
refresh(context);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
webServer.destroy();
}
throw ex;
}
}
// 下面这一步是是spring-core包内的方法,也是真正的refresh操作,属于AbstractApplicationContext类
// 故只在此做简要介绍,具体将在下一节。
public void refresh() throws BeansException, IllegalStateException {
this.startupShutdownLock.lock();
try {
this.startupShutdownThread = Thread.currentThread();
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// 为fresh做准备
prepareRefresh();
// 获取beanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备beanFactory,设置了一些配置,比如添加类加载器,设置忽略哪些类等
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// 重要:Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// 重要:Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 初始化消息源
initMessageSource();
// 初始化事件传播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// 检查监听器bean,并将其注册.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// 最后一步,发布对应的事件。Last step: publish corresponding event.
finishRefresh();
}
catch (RuntimeException | Error ex ) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
contextRefresh.end();
}
}
finally {
this.startupShutdownThread = null;
this.startupShutdownLock.unlock();
}
}
callRunners(...)
参数ConfigurableApplicationContext context, ApplicationArguments args
该方法中,会去执行context中的所有ApplicationRunner与CommandLineRunner。默认情况下是没有的,通过定义runner可以针对一些args做一些操作。
非核心,不细讲
总结
SpringBoot在启动阶段,主要是做了创建bootStrapContext临时使用,RunListener监听启动步骤,准备环境,读取多数据源配置,初始化ApplicationContext并根据初始化信息加载对应的类。