Springboot启动源码分析4——刷新容器
springboot启动源码分析1——初步初始化
springboot启动源码分析2——run方法分析
springboot启动源码分析3——环境配置
springboot启动源码分析4——刷新容器
springboot启动源码分析5——后续处理内容
Spring Boot启动源码分析6——refresh的invokeBeanFactoryPostProcessors方法详解
springboot启动源码分析7——自动配置
springboot启动源码分析8——aop
到了刷新容器这一步,前面的是springboot的相关处理工作,到了这一步,准确地来说springboot的准备处理工作已经完成了,剩下的工作是交给spring来完成的了。
try {
//根据参数创建出参数的application
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//4.配置容器的环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
//5.这里就是打印图标的地方,默认是会打印Spring,如果你设置了其他图标,那么就打印你设置的那一个图标
Banner printedBanner = printBanner(environment);
//6.创建一个容器
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
//7.为容器做准备
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//刷新容器
refreshContext(context);
//8.刷新容器后
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
在容器准备完成之后,就需要进行刷新容器了。
刷新容器方法:refreshContext。进入方法refreshContext进行查看
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//创建一个默认的跟踪在容器启动阶段进行标记和收集关于执行上下文的数据
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
//对上下文环境进行刷新和初始化,主要把servetContext和servletConfig放到容器propertySources中
//注意,在spring中有PropertySource和PropertySources,后者不是多个前者,后者是一个容器
//可以直接重写initPropertySources方法就好了
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//beanFactory创建好了之后还不能使用,必须要进行一些配置,这一步就是进行一些配置的
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();
}
}
}
刷新前准备
我们来看一下prepareRefresh();方法
@Override
protected void prepareRefresh() {
this.scanner.clearCache();
super.prepareRefresh();
}
主要是清除类路径中定义的bean的缓存。并且在清除缓存的时候,当地资源的缓存,如果是,那么就清除,如果不是,那么就把他便成为LocalResource类型的,并且限制大小为256(类型是LinkedHashMap),这里没有标明什么单位,只有一个256
接下来是调用父类的prepareRefresh()方法
具体如下
protected void prepareRefresh() {
// Switch to active.
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
//判断当前日志是否可以跟踪debug
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// Initialize any placeholder property sources in the context environment.
//注意,这里是初始化propertySources这个容器
//spring中有propertySource和propertySources,后者是一个容器,不是简单的多个前者
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
前面两步close和active的类型都是boolean,就是调向active类型
initPropertySources()分析
首先我们来看一下PropertySource