目录
八十四、原理解析-SpringApplication创建初始化流程
八十一、高级特性-Profile环境切换
1、Profile功能
为了方便多环节适配,springboot简化了profile功能
2、命令行激活
java -jar xxx.jar --spring.profiles.active=prod
3、配置文件分组
# 应用名称
spring.application.name=boot-09-features-profile
# 指定激活的环境。默认配置文件和指定环境的配置文件都会生效。
spring.profiles.active=myprod
# 分组
spring.profiles.group.myprod[0]=ppd
spring.profiles.group.myprod[1]=prod
spring.profiles.group.mytest[0]=test
八十二、高级特性-配置加载优先级
八十三、高级特性-自定义starter细节
1、starter启动原理
2、自定义starter
八十四、原理解析-SpringApplication创建初始化流程
1、SpringBoot原理,首先SpringBoot不是一个单纯的框架,它底层整合了非常多的技术,比如Spring、SpringMVC以及Spring整个系列技术栈的其它技术,甚至于第三方框架
2、需要理解Spring原理【Spring注解】,SpringMVC原理、自动配置原理、SpringBoot原理
3、前面给大家分析SpringBoot的自动配置原理,那我们接下来就来看我们整个SpringBoot应用是怎么启动起来的,它启动过程中又干了哪些活,接下来就研究一下启动原理
4、debug断点打在这里,这块启动原理,大家要额外注意一些,因为我们SpringBoot在启动整个应用的过程中,我们有非常多的时机,这些时机呢?我们有可能需要自定义干一些活,我们需要一些回调机制来完成这个事情,所以SpringBoot对底层这个能力也做了支持,所以我们在调试SpringBoot这个启动过程的时候,注意以下的这些东西,比如说有一个东西Listener(监听器)、Initializer(初始化器),它们都会在合适的时机进行回调,现在就来看一下整个启动过程。
5、我们来看一下SpringBoot应用是怎么启动的,我们已传入我们的主配置类就开始运行Spring的应用,怎么运行呢?我们step into进来,进来以后呢。它首先new Class把我们的主程序类传来,调用run方法,我们直接进入它的run方法,这个run方法呢?首先第一步会创建Spring应用,第二部把Spring的应用跑起来
6、首先我们来分析创建SpringApplication干了什么?我们来Step into,它给我们传入的呢是主配置类,我们来step into进来,然后它调用this有参构造器,传了两个参数。我们再step into进来
7、这个this里面呢,我们会看到初始化了很多东西,我们来往下走,这里来到SpringApplication里面的好多属性,我们要创建对象呢?这些属性先得初始化过来,我们先不管这些属性,我们再次来到下面这个类里面。首先呢,它先保存了第一个属性,this.resourceLoader(资源加载器),然后Assert.notNull('','')断言,如果没有主配置类会给你抛一个失败,然后把主配置类的信息先保存起来,在当前的SpringApplication这个类里面保存了当前主配置类是什么?(
Boot09HelloTestApplication
),当然这个主配置类里面,最核心的通过
@SpringBootApplication这个注解开启了整个自动配置功能,当然这一块我们就不用分析了。我们主配置类先记录了以后,接下来它再来在这保存了一些属性,所以呢?我们这个创建对象最核心的就是保存一些信息,这些信息包括些什么呢?
8、我们来看一些关键的,这个信息其中里面还有一个叫webApplicationType,它来决定当前的这个应用的web应用的类型,它调用了WebApplicationType.deduceFromClasspath(),它来判断我们web应用的类型,Step into进来,看是怎么判断的。用类工具类ClassUtils,我们看源码底层的时候,会看到非常多有用的工具类,ClassUtils会先来判断当前这个系统里面有没有导入reactive,相当于响应式编程的整个请求处理器,但是呢,我们现在不是响应式编程,如果是响应式编程,我们整个应用就会返回它是一个响应式编程。而我们现在是原生的servlet编程,所以呢,接下来就会返回
WebApplicationType.SERVLET,当前web应用的类型是servlet
9、this.bootstrappers这相当于一些初始启动器,什么叫做初始启动器呢?项目一启动我们就要干什么的,而这些初始启动器呢?它是这么来做的 getSpringFactoriesInstances 它是调用这个方法把我们这个类型传过去,在this.bootstrappers这里记录一下,它还是list,list里面都是Bootstrapper,它想要获取所有的初始化启动器,初始启动器是什么?我们看它是怎么获取的,step into进来
private List<Bootstrapper> bootstrappers;
10、它调用getSpringFactoriesInstances这个方法获取的,我们再来点进来,它相当于想要获取这个Bootstrapper,我们再step into进来。
它先拿到类加载器,然后呢使用一个SpringFactoriesLoader,这个东西呢,我们以前在讲自动配置原理的时候讲过,这个东西呢?SpringFactoriesLoader就是从spring.factories那个文件里面来读取这些类,所以我们来看一下,它是要读取哪些类呢?它是要读取Bootstrapper,但是呢,names等于0,它相当于会去来找所有的spring.factories这个文件里面看你有没有在这配置称为Bootstrapper类型。所以,我们如果想要项目一启动以后有一些启动类,所以我们在这一块找寻办法就是去spring.factories文件中找这个org.springframework.boot.Bootstrapper类型的,没有我们就不管,说明呢,我们当前类里面所有的jar包里面都没人配置这个东西,那如果有配置的话,会把这些东西创建一个实例,把这些实例给我们返回,而现在没有任何实例,说明我们的项目初始化启动没有任何初始化的引导器
11、这个东西我们先不管,现在又有一个核心叫setInitializers(翻译过来叫初始化器),它还是这个方法getSpringFactoriesInstances,如果大家以后呢,都遇到这个方法,都是去spring.factories这个文件里面去来找,看你有没有相关配置这个类,它的值是什么。你如果在系统里面配了一个叫ApplicationContextInitializer它就能给我们找到,如果找到了以后呢,会set保存起来,保存为initializers这个属性,它不仅找到了,还找到了7个
①、spring.factories找到ApplicationContextInitializer
②、源码
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();
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
12、叫setListeners, 它又找一些监听器叫ApplicationListener(应用监听器->一般就是来监听当前应用状态的),去spring.factories找 ApplicationListener,找到以后最终保存到下面的代码里面
①、
private List<ApplicationListener<?>> listeners;
②、SpringBoot应用启动保存的初始化器和监听器
13、this.mainApplicationClass = deduceMainApplicationClass();决定哪个是我们的主程序,它是通过找到堆栈,哪个是有main方法的,有main方法的就是主程序。这个就是我们SpringApplication应用创建做了那么一堆。简单来说,应用创建的过程,就是把一些关键的组件给我们去读取信息读取来,先提前保存到SpringApplication里面,保存到这里未来肯定是有用。接下来,看看SpringApplication创建完以后,run的时候干了哪些活。
八十五、原理解析-SpringBoot完整启动过程
1、接着前面,我们step into进入这个run方法。大家注意这个run方法其实还传了一个args,这个args说的就是我们,以前呢,这个jar包一启动,我们可能通过命令行传一些参数,不就是这些args吗?所以大家就可想而知,在这步args能传进来,那未来我们对命令行参数的解析可能也会得到。
2、我们step into进来,整个run的流程就这么多。首先第一个,它准备一个StopWatch(停止的监听器),这相当于new了一个对象StopWatch,相当于我们来监控整个应用的启停的,StopWacth呢上来调用了一个方法start()
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, 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, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
3、start()方法step into进来,发现这个方法啊,非常的简单,给StopWatch里面保存一些信息(像当前我们任务的名字,以及我们当前的时间(纳秒时间)),把这些信息呢给我们保存起来,像它们在这里给我们记录了一下,我们是哪一时刻启动的,应用的启动时间记录了以后呢
public void start() throws IllegalStateException {
this.start("");
}
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
} else {
this.currentTaskName = taskName;
this.startTimeNanos = System.nanoTime();
}
}
4、createBootstrapContext相当于创建引导上下文,所谓的上下文就是我们以后见到什么的Context都是我们当前引导环境上下文,我们整个应用的引导启动环境它里面有哪些就是在这里创建的,我们来看看,创建引导上下文,它里面又做了什么?进来以后,我们会发现它在这里创建了一个默认引导上下文,这个对象默认以后就会给里面保存很多的信息,这个信息呢,就会在这个上下文环境(DefaultBootstrapContext )里面保存,这里有一个关键叫this.bootstrappers,然后呢,这个bootstrappers调一个forEach(),这个bootstrappers是什么呢?是下面的属性(private List<Bootstrapper> bootstrappers;),所以我们之前呢,会在配置文件中,如果你配了Bootstrapper
private List<Bootstrapper> bootstrappers;
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));
return bootstrapContext;
}
5、Bootstrapper点击来,Bootstrapper是这个接口。如果人家一开始找到了你这个Bootstrapper,相当于我们的整个引导启动器找到了以后呢,接下来它就会调用这个我们的这个方法createBootstrapContext,你如果自定义了引导启动器,它会把所有的引导启动器forEach遍历一遍,然后呢?每一个引导启动器都会调用它的intitialize方法,因为我们看到引导启动器的接口有一个方法叫intitialize,它传入我们这个引导的注册工厂,然后呢,这个工厂里面我们就可以给它加入一些当前上下文的引导环境信息,所以我们这一块的核心就是 获取到所有之前的 bootstrappers 挨个执行 intitialize() 来完成对引导启动器上下文环境设置 我们现在这个引导启动器一个都没有,如果你想写了,你可以在spring.factories里面写一个
public interface Bootstrapper {
/**
* Initialize the given {@link BootstrapRegistry} with any required registrations.
* @param registry the registry to initialize
*/
void intitialize(BootstrapRegistry registry);
}
6、让当前应用进入headless模式。java.awt.headless
configureHeadlessProperty();
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
7、这里的 getRunListeners,会在
SpringApplicationRunListeners listeners这里保存一下
①、获取所有 RunListener(运行监听器)【为了方便所有Listener进行事件感知】
②、getSpringFactoriesInstances 去spring.factories找 SpringApplicationRunListener.
SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
this.applicationStartup);
}
③、找到了SpringApplicationRunListener并且保存到了SpringApplicationRunListeners
8、遍历 SpringApplicationRunListener 调用 starting 方法;
①、相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
listeners.starting(bootstrapContext, this.mainApplicationClass);
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
(step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
9、保存命令行参数;ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
10、准备环境 prepareEnvironment();
①、返回或者创建基础环境信息对象。StandardServletEnvironment
②、配置环境信息对象。(读取所有的配置源的配置属性值。)
③、绑定环境信息
④、监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
configureAdditionalProfiles(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast);
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
doWithListeners("spring.boot.application.environment-prepared",
(listener) -> listener.environmentPrepared(bootstrapContext, environment));
}
11、创建IOC容器(createApplicationContext())
ConfigurableApplicationContext context = null;
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
12、根据项目类型(Servlet)创建容器,
当前会创建 AnnotationConfigServletWebServerApplicationContext
/**
* A default {@link ApplicationContextFactory} implementation that will create an
* appropriate context for the {@link WebApplicationType}.
*/
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
13、准备ApplicationContext IOC容器的基本信息 prepareContext()
①、保存环境信息
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
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());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
②、IOC容器的后置处理流程。
/**
* Apply any relevant post processing the {@link ApplicationContext}. Subclasses can
* apply additional processing as required.
* @param context the application context
*/
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
14、应用初始化器;applyInitializers;
①、遍历所有的 ApplicationContextInitializer 。调用 initialize.。来对ioc容器进行初始化扩展功能
/**
* Apply any {@link ApplicationContextInitializer}s to the context before it is
* refreshed.
* @param context the configured ApplicationContext (not refreshed yet)
* @see ConfigurableApplicationContext#refresh()
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
15、遍历所有的 listener 调用 contextPrepared。EventPublishRunListenr;通知所有的监听器contextPrepared
listeners.contextPrepared(context);
void contextPrepared(ConfigurableApplicationContext context) {
doWithListeners("spring.boot.application.context-prepared", (listener) -> listener.contextPrepared(context));
}
16、所有的监听器 调用 contextLoaded。通知所有的监听器 contextLoaded
listeners.contextLoaded(context);
void contextLoaded(ConfigurableApplicationContext context) {
doWithListeners("spring.boot.application.context-loaded", (listener) -> listener.contextLoaded(context));
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
doWithListeners(stepName, listenerAction, null);
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
17、刷新IOC容器。refreshContext
①、创建容器中的所有组件(Spring注解)
refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
refresh((ApplicationContext) context);
}
/**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
* @deprecated since 2.3.0 in favor of
* {@link #refresh(ConfigurableApplicationContext)}
*/
@Deprecated
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
refresh((ConfigurableApplicationContext) applicationContext);
}
/**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
*/
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw ex;
}
}
②、最重要的代码,ioc容器单例实例化
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
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();
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException 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 {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
contextRefresh.end();
}
}
}
18、容器刷新完成后工作?afterRefresh
/**
* Called after the context has been refreshed.
* @param context the application context
* @param args the application arguments
*/
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
19、所有监听 器 调用 listeners.started(context); 通知所有的监听器 started
listeners.started(context);
20、调用所有runners;callRunners()
①、获取容器中的 ApplicationRunner
②、获取容器中的 CommandLineRunner
③、合并所有runner并且按照@Order进行排序
④、遍历所有的runner。调用 run 方法
callRunners(context, applicationArguments);
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);
}
}
}
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
@FunctionalInterface
public interface ApplicationRunner {
/**
* Callback used to run the bean.
* @param args incoming application arguments
* @throws Exception on error
*/
void run(ApplicationArguments args) throws Exception;
}
21、如果以上有异常,调用Listener 的 failed
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
SpringApplicationRunListeners listeners) {
try {
try {
handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
}
finally {
reportFailure(getExceptionReporters(context), exception);
if (context != null) {
context.close();
}
}
}
catch (Exception ex) {
logger.warn("Unable to close ApplicationContext", ex);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
22、调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
running如果有问题。继续通知 failed 。调用所有 Listener 的 failed;通知所有的监听器 failed
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
23、总结
①、
②、
③、