注意关注:几个重要的事件回调机制
这两个需要配置在META-INF/spring.factories:ApplicationContextInitializer、SpringApplicationRunListener
这两个只需要放在ioc容器中:ApplicationRunner、CommandLineRunner
此次讲解的SpringBoot版本是:2.2.6
1、启动配置流程讲解
SpringApplication.run(主程序类)
new SpringApplication(主程序类)
•判断是否web应用
•加载并保存所有ApplicationContextInitializer(META-INF/spring.factories),
•加载并保存所有ApplicationListener
•获取到主程序类
run()
•回调所有的SpringApplicationRunListener(META-INF/spring.factories)的starting
•获取ApplicationArguments
•准备环境&回调所有监听器(SpringApplicationRunListener )的environmentPrepared
•打印banner信息
•创建ioc容器对象
- AnnotationConfigEmbeddedWebApplicationContext(web环境容器)
- AnnotationConfigApplicationContext(普通环境容器)
•准备环境
–执行ApplicationContextInitializer. initialize()
–监听器SpringApplicationRunListener回调contextPrepared
–加载主配置类定义信息
–监听器SpringApplicationRunListener回调contextLoaded
•刷新启动IOC容器;
–扫描加载所有容器中的组件
–包括从META-INF/spring.factories中获取的所有EnableAutoConfiguration组件
•回调容器中所有的ApplicationRunner、CommandLineRunner的run方法
•监听器SpringApplicationRunListener回调finished
启动流程主要分两步:1、创建SpringApplication对象 2、执行run方法;
程序入口:主程序的main方法:
public static void main(String[] args) {
SpringApplication.run(SpringBoot07StarterApplication.class, args);
}
打断点之后,debug进去:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
1.1 创建SpringApplication对象
我们先来看:new SpringApplication(primarySources) 创建SpringApplication对象:
public SpringApplication(Class<?>... primarySources) {
//调用的就是下面的SpringApplication方法
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");
//保存主配置类到 primarySources 中
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
//判断当前是否是web应用,参加 1.1.1
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//从各个jar包的类路径下找到META-INF/spring.factories文件配置的所有ApplicationContextInitializer;然后保存起来,参加1.1.2
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
//从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener 参加1.1.2
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
//从多个配置类中找到有 main 方法的主配置类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
1.1.1 判断是否web应用的方法
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
//非web应用
return NONE;
}
}
//表示是web应用
return SERVLET;
}
}
NONE:非web应用, SERVLET:是web应用。
1.1.2 保存类路径下所有ApplicationContextInitializer 方法
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();
//(SpringFactoriesLoader.loadFactoryNames 加载类路径下的 META-INF/spring.factories
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
//SpringFactoriesLoader 类有如下方法
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 {
//找的路径就是:META-INF/spring.factories
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);
}
}
}
只有一个web模块的SpringBoot,默认加载的 ApplicationContextInitializer 是:
默认加载的 ApplicationListener 是:
1.2 运行run方法
扫描所有的组件,扫描所有的@Bean注解等,给我们初始化
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//从类路径下META-INF/spring.factories 获取SpringApplicationRunListeners;
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//回调所有的获取到的SpringApplicationRunListener的starting()方法
listeners.starting();
Collection exceptionReporters;
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境 详情参加:1.2.1
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
//打印Banner,就是启动的时候 SpringBoot的标识
Banner printedBanner = this.printBanner(environment);
//创建ApplicationContext;决定创建web的ioc还是普通的ioc
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
//准备上下文环境;将environment保存到ioc中;而且调用方法:applyInitializers();
//applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
//回调所有的SpringApplicationRunListener的contextPrepared(); 参加 1.2.3
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);
//扫描,创建,加载所有组件的地方;(配置类,组件,自动配置),详情参加:1.2.4
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);
}
//所有的SpringApplicationRunListener回调finished方法
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
//整个SpringBoot应用启动完成以后返回启动的ioc容器;
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
1.2.1 准备环境的方法
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach((Environment)environment);
//创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
listeners.environmentPrepared((ConfigurableEnvironment)environment);
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
listeners.environmentPrepared((ConfigurableEnvironment)environment); 表示环境准备完成
1.2.2 创建ApplicationContext(IOC)的方法
创建ApplicationContext;决定创建web的ioc还是普通的ioc,如果是web的ioc,返回就是:org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
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);
}
}
//使用反射创建 IOC 容器
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
SERVLET:表示web应用
1.2.3 准备上下文环境的方法
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
//回调之前保存的所有的ApplicationContextInitializer的initialize方法
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运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
listeners.contextLoaded(context);
}
this.applyInitializers(context);:回调之前保存的所有的ApplicationContextInitializer的initialize方法
listeners.contextPrepared(context);:回调所有的SpringApplicationRunListener的contextPrepared() listeners.contextLoaded(context); :回调所有的SpringApplicationRunListener的contextLoaded();
1.2.4 刷新容器,IOC容器初始化方法
调用的是父类:AbstractApplicationContext 的 refresh方法
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
1.3 事件监听机制
这两个需要配置在META-INF/spring.factories:ApplicationContextInitializer、SpringApplicationRunListener
这两个只需要放在ioc容器中:ApplicationRunner、CommandLineRunner
下面我们就来测试一下这四个;
使用SpringBoot的向导创建一个简单的web工程;在resources下面创建META-INF/spring.factories,因为要配置:ApplicationContextInitializer、SpringApplicationRunListener
1.3.1 自定义ApplicationContextInitializer
package com.springboot.starter.initializer;
import com.springboot.starter.service.HelloService;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class ZdwApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
/**
* HelloService helloService = configurableApplicationContext.getBean(HelloService.class);
* 在这里无法拿到自定义的Bean,此时还没有刷新IOC容器,只是做了SpringApplication的初始化操作,
* 如果从这里取,就会报错:java.lang.IllegalStateException: org.springframework.boot.web.servlet.context.
* AnnotationConfigServletWebServerApplicationContext@176b75f7 has not been refreshed yet
*/
System.out.println("-----------------------监听IOC容器的启动-----------------------");
}
}
1.3.2 自定义SpringApplicationRunListener
package com.springboot.starter.listener;
import com.springboot.starter.service.HelloService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
public class ZdwSpringApplicationRunListeners implements SpringApplicationRunListener {
private final SpringApplication application;
private final String[] args;
public ZdwSpringApplicationRunListeners(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
}
@Override
public void starting() {
System.out.println("ZdwSpringApplicationRunListeners...starting...");
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
String osname = environment.getProperty("os.name");//可以拿到操作系统的名称
System.out.println("ZdwSpringApplicationRunListeners...environmentPrepared..."+osname);
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
//HelloService helloService = context.getBean(HelloService.class); 这里也无法拿到自定义的Bean
System.out.println("ZdwSpringApplicationRunListeners...contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
//HelloService helloService = context.getBean(HelloService.class); 这里也无法拿到自定义的Bean
System.out.println("ZdwSpringApplicationRunListeners...contextLoaded...");
}
@Override
public void started(ConfigurableApplicationContext context) {
//这里可以拿到自定义的Bean,因为他是在 this.refreshContext(context); 刷新容器之后运行的
//刷新ioc容器的时候,会加载所有的自定义Bean
HelloService helloService = context.getBean(HelloService.class);
System.out.println("ZdwSpringApplicationRunListeners...started..."+helloService);
}
@Override
public void running(ConfigurableApplicationContext context) {
HelloService helloService = context.getBean(HelloService.class);
System.out.println("ZdwSpringApplicationRunListeners...running..."+helloService);
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
HelloService helloService = context.getBean(HelloService.class);
System.out.println("ZdwSpringApplicationRunListeners...failed..."+helloService);
}
}
1.3.3 自定义ApplicationRunner
package com.springboot.starter.runner;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component //添加到容器中
public class ZdwApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ZdwApplicationRunner...run------");
}
}
1.3.4 自定义CommandLineRunner
package com.springboot.starter.runner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component //添加到容器中
public class ZdwCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("ZdwCommandLineRunner...run=======");
}
}
启动主程序类,看控制台打印:
ZdwSpringApplicationRunListeners...starting...
ZdwSpringApplicationRunListeners...environmentPrepared...Windows 10
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.6.RELEASE)
-----------------------监听IOC容器的启动-----------------------
ZdwSpringApplicationRunListeners...contextPrepared...
2020-04-04 19:57:29.803 INFO 6600 --- [ main] c.s.s.SpringBoot07StarterApplication : Starting SpringBoot07StarterApplication on DESKTOP-INR89GT with PID 6600 (D:\IDEA\IDEA20190301_WorkSpace\SpringBoot\spring-boot-07-starter\target\classes started by ZDW in D:\IDEA\IDEA20190301_WorkSpace\SpringBoot\spring-boot-07-starter)
2020-04-04 19:57:29.824 INFO 6600 --- [ main] c.s.s.SpringBoot07StarterApplication : No active profile set, falling back to default profiles: default
ZdwSpringApplicationRunListeners...contextLoaded...
2020-04-04 19:57:31.505 INFO 6600 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-04-04 19:57:31.517 INFO 6600 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-04-04 19:57:31.517 INFO 6600 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-04-04 19:57:31.636 INFO 6600 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-04-04 19:57:31.637 INFO 6600 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1692 ms
2020-04-04 19:57:31.848 INFO 6600 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-04 19:57:32.034 INFO 6600 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-04-04 19:57:32.039 INFO 6600 --- [ main] c.s.s.SpringBoot07StarterApplication : Started SpringBoot07StarterApplication in 2.919 seconds (JVM running for 5.544)
ZdwSpringApplicationRunListeners...started...com.springboot.starter.service.HelloService@44d70181
ZdwApplicationRunner...run------
ZdwCommandLineRunner...run=======
ZdwSpringApplicationRunListeners...running...com.springboot.starter.service.HelloService@44d70181
1.4 总结
•Spring Boot启动扫描所有jar包的META-INF/spring.factories中配置的EnableAutoConfiguration组件
•spring-boot-autoconfigure.jar\META-INF\spring.factories有启动时需要加载的EnableAutoConfiguration组件配置
•配置文件中使用debug=true可以观看到当前启用的自动配置的信息
•自动配置会为容器中添加大量组件
•Spring Boot在做任何功能都需要从容器中获取这个功能的组件
•Spring Boot 总是遵循一个标准;容器中有我们自己配置的组件就用我们配置的,没有就用自动配置默认注册进来的组件;