Spring Boot 7. 启动配置原理
Spring Boot启动配置原理
启动原理、运行流程、自动配置原理
springboot 2.2.1
代码地址
ssh git@gitee.com:Ding_DangMao/learn-spring-bot.git
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.2.1.RELEASE</version>
</parent>
一、启动原理
- 几个重要的事件回调机制
配置在 META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
只需要放在 ioc容器中
ApplicationRunner
CommandLinerRunner
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//1. 创建 SpringApplication对象 2. 运行 run方法
return (new SpringApplication(primarySources)).run(args);
}
运行流程
-
创建 SpringApplication对象
public SpringApplication(Class<?>... primarySources) { this((ResourceLoader)null, primarySources); } 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应用 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //初始化;从类路径下找到 /meta-inf/spring.factories配置的所有 applicationContextInitializer;然后保存起来 this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); //从类路径下找到 /meta-inf/spring.factories 找打配置的所有 ApplicationListener保存起来 this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); //从多个配置类中找到有 main方法主配置类 this.mainApplicationClass = this.deduceMainApplicationClass(); } //保存主配置类 public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList(initializers); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return this.getSpringFactoriesInstances(type, new Class[0]); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = this.getClassLoader(); Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { 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); } } }
-
运行 run方法
SpringApplication.run(主程序类)
public ConfigurableApplicationContext run(String... args) {
//开始停止的监听
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//ioc容器
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
//System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless)));
this.configureHeadlessProperty();
//获取 SpringApplicationRunListeners !!!从类路径下 META-INF/spring.factories
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//回调所有的 SpringApplicationRunListener的 listener.starting();方法
listeners.starting();
Collection exceptionReporters;
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境 =》创建环境完成后回调 SpringApplicationRunListener的
//listener.environmentPrepared(environment);方法表示环境准备完成
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
// 打印 beaninfo 图标 System.setProperty("spring.beaninfo.ignore", ignore.toString());
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
//创建 ApplicationContext!!!决定创建那种类型的 ioc容器
context = this.createApplicationContext();
//异常分析报告的
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//准备上下文环境:将 environment 保存到 ioc中,而且 applyInitializers()
//applyInitializers(context) 回调之前保存的所有 ApplicationContextInitializer的initialize(context);方法
//contextPrepared(context) 回调所有的 SpringApplicationRunListener的listener.contextPrepared(context);方法
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//prepareContext 完全运行完以后 回调所有的 SpringApplicationRun 的 contextLoaded(context);方法
//刷新上下文 ioc容器初始化,如果是 web应用还会创建嵌入式的 tomcat
//扫描,创建,加载所有组件的地方
this.refreshContext(context);
//从 ioc容器中获取所有的 applicationrunner 和 CommandLineRunner 的回调
//applicationrunner先回调 CommandLineRunner 后回调
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);
//整个 spring boot启动完成以后返回启动的 ioc容器
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
//创建 applicationContext对象
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
//主要决定创建 那种类型的 ioc容器
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);
}
}
//利用 BeanUtils工具 反射创建 ioc对象
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
//上下文环境
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//把我们刚刚创建的 environment 保存起来
context.setEnvironment(environment);
//后置处理:注册一些小组件
this.postProcessApplicationContext(context);
//回调之前保存的所有的 ApplicationContextInitializer的initialize(context);方法
this.applyInitializers(context);
//回调所有的 SpringApplicationRunListener的listener.contextPrepared(context);
listeners.contextPrepared(context);
//日志记录
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
//命令行参数注册进来
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
//包括打印的 springBootBanner 注册
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]));
listeners.contextLoaded(context);
}
protected void applyInitializers(ConfigurableApplicationContext context) {
Iterator var2 = this.getInitializers().iterator();
//获取所有的 Initializers 调用 initialize(context);方法
//ApplicationContextInitializer 哪里有的那?就是第一次创建 springapplication对象的时候已经拿到了
while(var2.hasNext()) {
ApplicationContextInitializer initializer = (ApplicationContextInitializer)var2.next();
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
- new SpringApplication(主程序内)
判断是否web应用
加载并保存所有 ApplicationContextInitializer(META-INF/spring.factories)
加载并保存所有 ApplicationListener
获取到主程序类 - run()
回调所有的 SpringApplicationRunListener(META-INF/spring.factories)的starting
获取 ApplicationArguments
准备环境&回调所有监听器(SpringApplicationRunListener)的environmentPrepared
打印 banner对象
创建ioc容器对象
- AnnotationConfigEmbeddedWebApplicationContext(web环境容器)
- AnnotationConfigApplicationContext(普通环境容器) - run()
- 准备环境
- 执行ApplicationContextInitializer. initialize()
- 监听器SpringApplicationRunListener回调contextPrepared
- 加载主配置类定义信息
- 监听器SpringApplicationRunListener回调conatextLoaded
- 刷新启动IOC容器;
- 扫描加载所有容器中的组件
– 包括从META-INF/spring.factories中获取的所有EnableAutoConfiguration组件 - 回调容器中所有的ApplicationRunner、CommandLineRunner的run方法
- 监听器SpringApplicationRunListener回调finished
二、事件监听机制 和配置
- Spring Boot启动扫描所有jar包的META-INF/spring.factories中配置的EnableAutoConfiguration组件
- spring-boot-autoconfigure.jar\META-INF\spring.factories有启动时需要加载的EnableAutoConfiguration组件配置
- 配置文件中使用debug=true可以观看到当前启用的自动配置的信息
- 自动配置会为容器中添加大量组件
- Spring Boot在做任何功能都需要从容器中获取这个功能的组件
- Spring Boot 总是遵循一个标准;容器中有我们自己配置的组件就用我们配置的,没有就用自动配置默认注册进来的组件;
@Component
public class HelloApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("HelloApplicationRunner ::" + args);
}
}
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("HelloCommandLineRunner::" + Arrays.toString(args));
}
}
public class HelloApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.println("HelloApplicationContextInitializer方法运行了::" + configurableApplicationContext);
}
}
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
public HelloSpringApplicationRunListener(SpringApplication application, String[] args) {
System.out.println("constructor");
}
@Override
public void starting() {
System.out.println("starting...");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("environmentPrepared...");
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("contextLoaded...");
}
@Override
public void started(ConfigurableApplicationContext context) {
System.out.println("started...");
}
@Override
public void running(ConfigurableApplicationContext context) {
System.out.println("running...");
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("failed...");
}
}
META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=\
com.cainiao100.springboot.listener.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
com.cainiao100.springboot.listener.HelloSpringApplicationRunListener
运行结果
constructor
starting...
environmentPrepared...
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.1.RELEASE)
HelloApplicationContextInitializer方法运行了::org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@50a638b5, started on Thu Jan 01 08:00:00 CST 1970
contextPrepared...
2021-11-14 14:22:35.371 INFO 1940 --- [ main] c.cainiao100.springboot.SpringBootMain : Starting SpringBootMain on DESKTOP-PHJGSH5 with PID 1940 (D:\code\springboot\spring-boot-07\target\classes started by KAlways18 in D:\code\springboot)
2021-11-14 14:22:35.378 INFO 1940 --- [ main] c.cainiao100.springboot.SpringBootMain : No active profile set, falling back to default profiles: default
contextLoaded...
2021-11-14 14:22:37.411 INFO 1940 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-11-14 14:22:37.424 INFO 1940 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-11-14 14:22:37.425 INFO 1940 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.27]
2021-11-14 14:22:37.650 INFO 1940 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-11-14 14:22:37.650 INFO 1940 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2124 ms
2021-11-14 14:22:37.849 INFO 1940 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-11-14 14:22:38.107 INFO 1940 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-11-14 14:22:38.112 INFO 1940 --- [ main] c.cainiao100.springboot.SpringBootMain : Started SpringBootMain in 3.577 seconds (JVM running for 5.33)
started...
HelloApplicationRunner ::org.springframework.boot.DefaultApplicationArguments@3ae66c85
HelloCommandLineRunner::[]
running...