SpringBoot启动配置原理
SpringBoot最大的优势就是自动化配置,只需将需要的配置按照一定的规则,加上@Configuration @ConfigurationProperties @EnableAutoConfigurations
注解,便可以自动将需要的数据加入到IOC容器中。使用非常方便快捷,同时降低代码量,无需XML文件另外进行配置。那么SpringBoot底层到底做了哪些工作,它的启动原理,运行流程,自动配置到底是怎么样,下面简要分析一下。
1 SpringBoot启动配置原理
SpringBoot启动配置主要关注点是几个重要的事件回调机制 ApplicationContextInitializer ,SpringApplicationRunListener,ApplicationRunner,CommandLineRunner
,下面我们从主配置类@SpringBootApplication
的main方法进行启动流程分析。
- 启动流程:
1、通过main方法创建SpringApplication对象,再执行run()方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
那么创建SpringApplication是怎么样的呢?在创建过程中都干了什么?分析如下:
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//这一步是将主配置类保存起来
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources))*;
//这一步是判断当前环境是不是web环境,里面有Servlet,所以是web环境
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//这一步非常关键,是获取所有WEB-INF/spring.factories下面的初始化器,如下是加载的方法
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//这一步是获取所有的WEB-INF/spring.factories下面的Listener监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//最后一步,获取启动类,也就是SpringBoot的main方法
this.mainApplicationClass = this.deduceMainApplicationClass();
}
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") :
//这里就是加载所有的Initializer
ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
然后SpringApplication进行反射创建对象,集合如下图:
获取监听器代码示例:
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
下面是获取到的监听器全类名:
可以看到,上述SpringBoot在创建SpringApplication的对象过程还是比较复杂,但是最重要还是实例化自己的一些属性信息。下面看一看到底执行run()方法的启动流程是怎么样的,
- SpringApplication的run()方法:
public ConfigurableApplicationContext run(String... args) {
//把停止的监听启动
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//空的IOC容器
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//重点来了,获取SpringApplicationRunListeners ,并将它启动(EventPubshingRunListener)。这里的获取方式和上一步一样,都是在类路径下META-INF/spring.factories下面获取。
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//这里的starting()是启动所有之前获取到的监听器,starting()代码如下:
listeners.starting();
Collection exceptionReporters;
try {
//这里表明是将命令行参数用ApplicationArguments 进行封装了一下
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
//创建环境完成后回调SpringApplicationRunListener
this.configureIgnoreBeanInfo(environment);
//这一步就是打印Banner图标
Banner printedBanner = this.printBanner(environment);
//非常关键的一步,创建IOC容器,创建过程如下面代码,主要是分析是不是web环境,SpringBoot创建的是`AnnotationConfigServletWebServerApplication`
context = this.createApplicationContext();
//出现异常的回调方式
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//这里又是非常重要的一步,IOC容器创建完成后,准备上下文
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新环境,刷新容器,Spring里面最重要的过程,扫描所有的初始化类,IOC初始化过程,前置通知、后置通知、单实例Bean(这个是IOC容器的重中之重),同时,如果是web应用,这里面还会初始化嵌入式的Tomcat
this.refreshContext(context);
//从IOC容器中获取所有的ApplicationRunner和CommandLineRunner进行回调。同时调用Spring的finish方法
this.afterRefresh(context, applicationArguments);
//保存应用状态,启动完成
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
//启动完成后,返回IOC容器
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
starting()代码块:
Iterator var1 = this.listeners.iterator();
while(var1.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
listener.starting();
}
}
下面可以看到listeners
创建IOC容器
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
IOC容器创建完成后准备上下文
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//准备上下文第一步就是把environment保存到IOC容器中
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
//这一步非常重要,就是把我们第一次创建SpringApplication的时候,将保存的Intializers实例进行回调
this.applyInitializers(context);
//回调之前保存好的SpringApplicationRunListener的contextPrepared()方法
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
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 = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
//prepareContext()方法完全完成以后,回调所有的contextLoaded()方法
listeners.contextLoaded(context);
}
总结
SpringBoot最重要的就是几个回调机制:
1. ApplicationContextInitialer
, SpringApplicationRunListener
这两个配置放在配置当中。
2. ApplicationRunner
,CommandRunner
(放入到IOC容器中)
3. 监听器的启动,初始化器的启动,IOC容器的刷新。