启动配置原理
几个重要的事件回调机制
- ApplicationContextInitializer
- SpringApplicationRunListener
- ApplicationRunner
- CommandLineRunner
启动流程
|  | return new SpringApplication(primarySources).run(args);
 | 
- 创建SpringApplication对象
- 运行run方法
创建对象
现在左边的参数是null
|  | public SpringApplication(Class<?>... primarySources) {this(null, primarySources);
 }
 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();
 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
 this.mainApplicationClass = deduceMainApplicationClass();
 }
 
 | 
第8行
判断这是哪一类web应用,web分三类
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | public enum WebApplicationType {
 
 
 
 
 NONE,
 
 
 
 
 
 SERVLET,
 
 
 
 
 
 REACTIVE;
 
 | 
|  | static WebApplicationType deduceFromClasspath() {if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
 && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
 return WebApplicationType.REACTIVE;
 }
 for (String className : SERVLET_INDICATOR_CLASSES) {
 if (!ClassUtils.isPresent(className, null)) {
 return WebApplicationType.NONE;
 }
 }
 return WebApplicationType.SERVLET;
 }
 
 | 
第9行
从类路径下找到所有的spring.factories配置的所有ApplicationConterxtInitializeer,然后保存
|  | private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);
 if (result != null) {
 return result;
 }
 
 try {
 Enumeration<URL> urls = (classLoader != null ?
 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
 result = new LinkedMultiValueMap<>();
 while (urls.hasMoreElements()) {
 
 | 
|  | public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
 | 
第10行
找listener
第11行
找到主配置类
|  | private Class<?> deduceMainApplicationClass() {try {
 StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
 for (StackTraceElement stackTraceElement : stackTrace) {
 if ("main".equals(stackTraceElement.getMethodName())) {
 return Class.forName(stackTraceElement.getClassName());
 }
 }
 }
 catch (ClassNotFoundException ex) {
 
 }
 return null;
 }
 
 | 
run
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 
 | 
 
 
 
 
 public ConfigurableApplicationContext run(String... args) {
 StopWatch stopWatch = new StopWatch();
 stopWatch.start();
 ConfigurableApplicationContext context = null;
 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
 configureHeadlessProperty();
 SpringApplicationRunListeners listeners = getRunListeners(args);
 listeners.starting();
 try {
 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
 configureIgnoreBeanInfo(environment);
 Banner printedBanner = printBanner(environment);
 context = createApplicationContext();
 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
 new Class[] { ConfigurableApplicationContext.class }, context);
 prepareContext(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;
 }
 
 | 
第12行
配置awt
第13-14行
获取listener, 然后回调所有的listener
|  | void starting() {for (SpringApplicationRunListener listener : this.listeners) {
 listener.starting();
 }
 }
 
 | 
第17行
准备环境, 获取环境,这里的getOrCreateEnvironment分三种,点进去就知道了, 获取后配置环境,之后回调listeners的环境准备完成函数listeners.environmentPrepared(environment);
|  | private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,ApplicationArguments applicationArguments) {
 
 ConfigurableEnvironment environment = getOrCreateEnvironment();
 configureEnvironment(environment, applicationArguments.getSourceArgs());
 ConfigurationPropertySources.attach(environment);
 listeners.environmentPrepared(environment);
 bindToSpringApplication(environment);
 if (!this.isCustomEnvironment) {
 environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
 deduceEnvironmentClass());
 }
 ConfigurationPropertySources.attach(environment);
 return environment;
 }
 
 | 
第19行
打印图标
第20行
利用反射,根据当前的web环境创建IOC容器,
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
 | 
 
 
 
 
 
 protected ConfigurableApplicationContext createApplicationContext() {
 Class<?> contextClass = this.applicationContextClass;
 if (contextClass == null) {
 try {
 switch (this.webApplicationType) {
 case SERVLET:
 contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
 break;
 case REACTIVE:
 contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
 break;
 default:
 contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
 }
 }
 catch (ClassNotFoundException ex) {
 throw new IllegalStateException(
 "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
 }
 }
 return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
 }
 
 | 
第23行
准备上下文环境, 在环境保存到ioc中,而且applyInitializers, 即回调之前保存的所有的initializer, 然后监听器调用环境准备完毕,之后就是打印日志、在IOC中注册Banner、命令行参数。最后回调监听器的环境加载完成
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
 | private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
 context.setEnvironment(environment);
 postProcessApplicationContext(context);
 applyInitializers(context);
 listeners.contextPrepared(context);
 if (this.logStartupInfo) {
 logStartupInfo(context.getParent() == null);
 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 = getAllSources();
 Assert.notEmpty(sources, "Sources must not be empty");
 load(context, sources.toArray(new Object[0]));
 listeners.contextLoaded(context);
 }
 
 | 
第24行
这里之前讲过,是加载IOC容器中的所有组件的,如果是web应用,还会创建嵌入式的tomcat
第31行
|  | 回调所有的runner,里面包含了APplicationRunner和CommandLineRunner, 优先调用前者private void callRunners(ApplicationContext context, ApplicationArguments args) {
 List<Object> runners = new ArrayList<>();
 runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
 runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
 AnnotationAwareOrderComparator.sort(runners);
 for (Object runner : new LinkedHashSet<>(runners)) {
 if (runner instanceof ApplicationRunner) {
 callRunner((ApplicationRunner) runner, args);
 }
 if (runner instanceof CommandLineRunner) {
 callRunner((CommandLineRunner) runner, args);
 }
 }
 }
 
 | 
run结束了
返回IOC容器