SpringBoot版本:2.1.1 ==》启动流程分析汇总
接上篇Spring Boot 2.1.1(十三)启动流程分析之准备应用上下文
目录
5、调用已注册的BeanFactoryPostProcessors Bean
先调用BeanDefinitionRegistryPostProcessors
8、注册SimpleApplicationEventMulticaster
11、完成beanFactory初始化,初始化所有剩余的单例bean
public ConfigurableApplicationContext run(String... args) {
....
try {
//本篇内容从本行开始记录
refreshContext(context);
//本篇内容记录到这,后续更新
....
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
}
流程分析
感觉这篇内容会有很多呀!怼它!来看源码。
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
最终调用父类AbstractApplicationContext的refresh()方法。
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
stopAndReleaseWebServer();
throw ex;
}
}
可以看到refresh中的步骤都是单个单个的方法,很方便看,下面一个一个方法讲。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备刷新
prepareRefresh();
// 通知子类刷新内部bean工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备bean工厂以便在此上下文中使用
prepareBeanFactory(beanFactory);
try {
// 允许上下文子类中对bean工厂进行后处理
postProcessBeanFactory(beanFactory);
// 在bean创建之前调用BeanFactoryPostProcessors后置处理方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 注册DelegatingMessageSource
initMessageSource();
// 注册multicaster
initApplicationEventMulticaster();
// 创建内置的Servlet容器
onRefresh();
// 注册Listener
registerListeners();
// 完成BeanFactory初始化,初始化剩余单例bean
finishBeanFactoryInitialization(beanFactory);
// 发布对应事件
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();
}
}
}
开始之前先把AnnotationConfigServletWebServerApplicationContext类图放这:
1、准备刷新
先调用子类重写的方法,再调用父类方法。还记得前面讲过在创建AnnotationConfigServletWebServerApplicationContext的时候构造方法中实例化了一个ClassPathBeanDefinitionScanner。
@Override
protected void prepareRefresh() {
this.scanner.clearCache();
super.prepareRefresh();
}
子类prepareRefresh()方法
在其父类ClassPathScanningCandidateComponentProvider中有一个MetadataReaderFactory(接口)工厂对象,判断该对象是否是CachingMetadataReaderFactory这个特定类或者是它的子类的一个实例,返回Boolean值,是就是true,则清除缓存。该类中有个Map,用来缓存每个Spring资源句柄(即每个“.class”文件)的MetadataReader实例。
缓存Map如果是LocalResourceCache(可以看到该类继承了LinkedHashMap),执行的就是LinkedHashMap的clear()方法了;
else如果缓存不为空,就是重新new一个LocalResourceCache。
父类prepareRefresh()方法
protected void prepareRefresh() {
//系统启动时间
this.startupDate = System.currentTimeMillis();
//是否关闭标识,false
this.closed.set(false);
//是否活跃标识,true
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// 调用子类GenericWebApplicationContext重写后的方法替换servlet相关属性源
initPropertySources();
// 这里是验证由ConfigurablePropertyResolver#setRequiredProperties()方法指定的属性,解析为非空值,如果没有设置的话这个方法就不会执行什么操作。
getEnvironment().validateRequiredProperties();
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
看下最后这个方法,initServlet属性源,方法注释是这么说的将基于Servlet的StubPropertySource替换为使用给定servletContext和servletConfig对象填充的实例。此方法可以被调用任意次数,但将用相应的实际属性源替换为StubPropertySource一次且仅一次。
看下if判断里的条件,servletContext不为空,source中存在指定name的的属性源,且该属性源要是StubPropertySource的类型或者是其子类。也就是当第一次调用以后,该属性源就被替换成了ServletContextPropertySource和ServletConfigPropertySource,所以之后的调用最后一个判断条件就不会成立了。
public static void initServletPropertySources(MutablePropertySources sources,
@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
Assert.notNull(sources, "'propertySources' must not be null");
//servletContextInitParams
String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
if (servletContext != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletContextPropertySource(name, servletContext));
}
//servletConfigInitParams
name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
}
}
看validateRequiredProperties()方法前,先看下Environment的类图。这里是验证由ConfigurablePropertyResolver#setRequiredProperties()方法指定的属性,解析为非空值,如果没有设置的话这个方法就不会执行什么操作,所以这里就略过了。
方法最后new了一个LinkedHashSet,收集早期事件,如果multicaster 有用就会广播事件。
prepareRefresh()就执行完了。
2、通知子类刷新内部bean工厂
可以看到refreshBeanFactory()方法上的注释说的,什么都不做:我们拥有一个内部beanfactory,并依靠调用方通过我们的公共方法(或beanfactory)注册bean。
前面介绍GenericApplicationContext说了与每次刷新创建新的内部beanfactory实例的其他applicationContext实现不同,此上下文的内部beanfactory从一开始就可用,以便能够在其上注册bean定义。只能调用一次refresh()。
所以在该方法内只设置了SerializationId,该id是在准备应用上下文时调用ContextIdApplicationContextInitializer时设置的id,在setSerializationId方法中,使用id做key,new了一个弱引用对象为value,添加到了serializableFactories中,DefaultListableBeanFactory为被弱引用对象;如果需要,可以通过id得到引用对象,在通过get()方法得到DefaultListableBeanFactory对象。
3、准备bean工厂
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//通知内部bean工厂使用上下文的类加载器
beanFactory.setBeanClassLoader(getClassLoader());