SpringBoot启动时都做了哪些事(三)?

简介: SpringBoot启动时都做了哪些事(三)?

本文我们开始分析应用上下文的创建和配置。核心流程梳理如下:

创建应用上下文实例AnnotationConfigServletWebServerApplicationContext,这里会同步创建AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner并注册一系

"AnnotationConfigProcessor"。其父类GenericApplicationContext的构造方法中会触发DefaultListableBeanFactory的实例化流程。

准备应用上下文

为应用上下文绑定环境实例

尝试为context设置ResourceLoader和ClassLoader,并为BeanFactory进行设置ConversionService

触发每一个ApplicationContextInitializer的initialize方法

listeners.contextPrepared(context);广播ApplicationContextInitializedEvent事件

注册单例springApplicationArguments—DefaultApplicationArguments

注册单例springBootBanner

设置是否允许BeanDefinition覆盖,默认是false

加载主启动应用类,注册启动类的BeanDefinition到容器DefaultListableBeanFactory

listeners.contextLoaded(context);触发EventPublishingRunListener的contextLoaded方法,广播ApplicationPreparedEvent事件

【1】创建应用上下文

如下所示如果contextClass 为null,那么根据webApplicationType来获取上下文class类型,然后使用BeanUtils实例化得到ConfigurableApplicationContext 。本文这里是AnnotationConfigServletWebServerApplicationContext。

//SpringApplication的createApplicationContext方法
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);
}

如下是三个应用上下文类型,分别针对不同的环境。

public static final String DEFAULT_CONTEXT_CLASS = 
"org.springframework.context.annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = 
"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = 
"org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

如下是三个应用上下文类型,分别针对不同的环境。

public static final String DEFAULT_CONTEXT_CLASS = 
"org.springframework.context.annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = 
"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = 
"org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

AnnotationConfigServletWebServerApplicationContext的构造方法中,初始化了reader和scanner。

public AnnotationConfigServletWebServerApplicationContext() {
  this.reader = new AnnotatedBeanDefinitionReader(this);
  this.scanner = new ClassPathBeanDefinitionScanner(this);
}

AnnotatedBeanDefinitionReader是ClassPathBeanDefinitionScanner的替代方案,用来显示注册bean。ClassPathBeanDefinitionScanner则是我们常见的bean扫描器,用来扫描classpath下的组件(@Component/@Repository/@Service/@Controller)并注册bean定义到容器中。这里我们要特别注意这个AnnotatedBeanDefinitionReader,如下所示其构造函数中触发了一些注解后置处理器。




public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
  Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
  Assert.notNull(environment, "Environment must not be null");
  this.registry = registry;
  this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
  // 这句话很重要
  AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

registerAnnotationConfigProcessors这个方法做了什么呢?我们这里先简单总结一下:

对 beanFactory 添加依赖比较器AnnotationAwareOrderComparator 和 自动装配解析器 ContextAnnotationAutowireCandidateResolver

注册ConfigurationClassPostProcessor 用于对 @Configuration 类进行引导处理

注册AutowiredAnnotationBeanPostProcessor 处理 @Autowired @Value 和 JSR-330的@Inject 还有 @Lookup 注解

注册CommonAnnotationBeanPostProcessor 用来处理 @PostConstruct @PreDestroy @Resource

PersistenceAnnotationBeanPostProcessor 用于 JPA场景

DefaultEventListenerFactory、EventListenerMethodProcessor 支持 @EventListener用于将注解方法注册为单独的ApplicationListener实例

本文这里只注册了如下五个:

// ConfigurationClassPostProcessor
0 = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
// AutowiredAnnotationBeanPostProcessor
1 = "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"
// CommonAnnotationBeanPostProcessor
2 = "org.springframework.context.annotation.internalCommonAnnotationProcessor"
//EventListenerMethodProcessor
3 = "org.springframework.context.event.internalEventListenerProcessor"
// DefaultEventListenerFactory
4 = "org.springframework.context.event.internalEventListenerFactory"

创建完上下文后就该获取异常报告器SpringBootExceptionReporter,这里getSpringFactoriesInstances我们再第一篇文章中讲过这里不再赘述。

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
          new Class[] { ConfigurableApplicationContext.class }, context);

【2】准备上下文

也就是prepareContext方法。这里会为context和BeanFactory做一些配置,触发每个ApplicationContextInitializer的initialize方法并广播ApplicationContextInitializedEvent、ApplicationPreparedEvent事件。

// SpringApplication
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
    SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
  // 为应用上下文绑定环境实例
  context.setEnvironment(environment);
  //尝试为context设置ResourceLoader和ClassLoader,并为BeanFactory进行设置ConversionService
  postProcessApplicationContext(context);
  // 触发每一个ApplicationContextInitializer的initialize方法
  applyInitializers(context);
  //广播ApplicationContextInitializedEvent事件
  listeners.contextPrepared(context);
  //打印启动信息
  if (this.logStartupInfo) {
    logStartupInfo(context.getParent() == null);
    logStartupProfileInfo(context);
  }
  // Add boot specific singleton beans
  ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  // 注册单例springApplicationArguments---DefaultApplicationArguments
  beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
  // 注册单例springBootBanner
  //org.springframework.boot.SpringApplicationBannerPrinter.PrintedBanner
  if (printedBanner != null) {
    beanFactory.registerSingleton("springBootBanner", printedBanner);
  }
  // 设置是否允许BeanDefinition覆盖,默认是false
  if (beanFactory instanceof DefaultListableBeanFactory) {
    ((DefaultListableBeanFactory) beanFactory)
        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
  }
  // 懒加载配置--本文这里是false
  if (this.lazyInitialization) {
    context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
  }
  // Load the sources 获取的类信息,本文这里只有主启动类
  Set<Object> sources = getAllSources();
  Assert.notEmpty(sources, "Sources must not be empty");
  //加载bean到应用上下文
  load(context, sources.toArray(new Object[0]));
  //发布并广播ApplicationPreparedEvent事件
  listeners.contextLoaded(context);
}

① postProcessApplicationContext

ApplicationContext的后置处理,主要尝试为其设置resourceLoader 、ClassLoader,并尝试为BeanFactory注册单例bean–internalConfigurationBeanNameGenerator及设置ConversionService。

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
// 尝试注册单例internalConfigurationBeanNameGenerator,本文这里为null
  if (this.beanNameGenerator != null) {
//注册单例,其实就是放入一级缓存和Set<String> registeredSingletons中
    context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
        this.beanNameGenerator);
  }
  // 本文这里为null 不进入方法
  if (this.resourceLoader != null) {
    if (context instanceof GenericApplicationContext) {
    // 设置资源加载器
      ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
    }
    if (context instanceof DefaultResourceLoader) {
    // 设置类加载器
      ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
    }
  }
  // 本文这里默认为true,为BeanFactory设置ConversionService
  if (this.addConversionService) {
  // 为BeanFactory设置转换服务
    context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
  }
}

② applyInitializers

本质是获取容器里面的所有ApplicationContextInitializer ,触发每一个的initialize方法。

protected void applyInitializers(ConfigurableApplicationContext context) {
// 这里得到是一个asUnmodifiableOrderedSet(this.initializers);,也就是只读的
  for (ApplicationContextInitializer initializer : getInitializers()) {
  // 这里获取的是接口泛型的类型,比如ConfigurableApplicationContext
    Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
        ApplicationContextInitializer.class);
    Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
    //触发每个初始化器的初始化方法
    initializer.initialize(context);
  }
}

我们在SpringBoot启动时都做了哪些事(一)?一文中分析SpringApplication实例过程中提到过ApplicationContextInitializer 的获取。

本文这里获取的有8个,对其进行遍历校验类型然后触发每个initializer 的方法initialize

0 = {SharedMetadataReaderFactoryContextInitializer@3564} 
1 = {DelegatingApplicationContextInitializer@3565} 
2 = {ContextIdApplicationContextInitializer@3566} 
3 = {ConditionEvaluationReportLoggingListener@3567} 
4 = {RestartScopeInitializer@3568} 
5 = {ConfigurationWarningsApplicationContextInitializer@3569} 
6 = {RSocketPortInfoApplicationContextInitializer@3570} 
7 = {ServerPortInfoApplicationContextInitializer@3571} 

SharedMetadataReaderFactoryContextInitializer


向应用上下文的List beanFactoryPostProcessors添加了CachingMetadataReaderFactoryPostProcessor。

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
  applicationContext.addBeanFactoryPostProcessor(new CachingMetadataReaderFactoryPostProcessor());
}

ConfigurationWarningsApplicationContextInitializer


向应用上下文的List beanFactoryPostProcessors添加了ConfigurationWarningsPostProcessor。

@Override
public void initialize(ConfigurableApplicationContext context) {
  context.addBeanFactoryPostProcessor(new ConfigurationWarningsPostProcessor(getChecks()));
}
protected Check[] getChecks() {
  return new Check[] { new ComponentScanPackageCheck() };
}

也就是说上面两个ApplicationContextInitializer增加了两个BeanFactoryPostProcessor。

ConditionEvaluationReportLoggingListener


其主要向AbstractApplicationContext的成员Set> applicationListeners添加了ConditionEvaluationReportListener。

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
  this.applicationContext = applicationContext;
  // 添加监听
  applicationContext.addApplicationListener(new ConditionEvaluationReportListener());
  if (applicationContext instanceof GenericApplicationContext) {
    // Get the report early in case the context fails to load
    // 得到(实例化)ConditionEvaluationReport并为其设置parent
    this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
  }
}

RSocketPortInfoApplicationContextInitializer


其主要向AbstractApplicationContext的成员Set> applicationListeners添加了RSocketPortInfoApplicationContextInitializer.Listener。

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
  applicationContext.addApplicationListener(new Listener(applicationContext));
}

ServerPortInfoApplicationContextInitializer


其主要向AbstractApplicationContext的成员Set> applicationListeners添加了ServerPortInfoApplicationContextInitializer。

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
  applicationContext.addApplicationListener(this);
}

上面三个ApplicationContextInitializer注册了三个ApplicationListener到应用上下文中。


DelegatingApplicationContextInitializer


尝试从环境配置信息获取context.initializer.classes对应的值,实例化那些bean并触发每个的initialize方法。本文这里没有任何动作。

@Override
public void initialize(ConfigurableApplicationContext context) {
  ConfigurableEnvironment environment = context.getEnvironment();
  // 从environment获取context.initializer.classes配置的信息
  // 本文这里为空,不触发任何动作
  List<Class<?>> initializerClasses = getInitializerClasses(environment);
  if (!initializerClasses.isEmpty()) {
    applyInitializerClasses(context, initializerClasses);
  }
}

ContextIdApplicationContextInitializer

获取应用上下文ID对象contextId ,为applicationContext设置ID并注册单例bean到BeanFactory中。

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
  ContextId contextId = getContextId(applicationContext);
  applicationContext.setId(contextId.getId());
  applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(), contextId);
}

RestartScopeInitializer

注册restart scope到BeanFactory的Map scopes中。

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
  applicationContext.getBeanFactory().registerScope("restart", new RestartScope());
}

③ listeners.contextPrepared(context)

本文this指的是SpringApplicationRunListeners,这里只有一个EventPublishingRunListener,将会广播ApplicationContextInitializedEvent事件。

void contextPrepared(ConfigurableApplicationContext context) {
  for (SpringApplicationRunListener listener : this.listeners) {
    listener.contextPrepared(context);
  }
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
  this.initialMulticaster
      .multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}

有如下三个监听器对ApplicationContextInitializedEvent事件感兴趣。

0 = {RestartApplicationListener@3945} 
1 = {BackgroundPreinitializer@3946} 
2 = {DelegatingApplicationListener@3947} 

logStartupInfo(context.getParent() == null);打印启动信息:

logStartupProfileInfo(context);打印信息:

④ load

这里将会加载主启动应用类,注册启动类的BeanDefinition到容器DefaultListableBeanFactory中。

protected void load(ApplicationContext context, Object[] sources) {
  if (logger.isDebugEnabled()) {
    logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
  }
  BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
  if (this.beanNameGenerator != null) {
    loader.setBeanNameGenerator(this.beanNameGenerator);
  }
  if (this.resourceLoader != null) {
    loader.setResourceLoader(this.resourceLoader);
  }
  if (this.environment != null) {
    loader.setEnvironment(this.environment);
  }
  loader.load();
}

创建BeanDefinitionLoader ,这里registry是AnnotationConfigServletWebServerApplicationContext实例。

protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
  return new BeanDefinitionLoader(registry, sources);
}

BeanDefinitionLoader构造器如下其配置了annotatedReader 、xmlReader以及scanner 。这三个在整个应用中是非常重要的基础设置,其完成了bean的扫描与beandefinition的注册。

BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
  Assert.notNull(registry, "Registry must not be null");
  Assert.notEmpty(sources, "Sources must not be empty");
  this.sources = sources;
  this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
  this.xmlReader = new XmlBeanDefinitionReader(registry);
  if (isGroovyPresent()) {
    this.groovyReader = new GroovyBeanDefinitionReader(registry);
  }
  this.scanner = new ClassPathBeanDefinitionScanner(registry);
  this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}

这里最终会如下所示,调用AnnotatedBeanDefinitionReader注册应用主类,也就是注册BeanDefinitionbeanDefinitionMap中并记录beanNamebeanDefinitionNames中。


⑤ listeners.contextLoaded

如下所示,这里实际上会触发EventPublishingRunListenercontextLoaded方法。

void contextLoaded(ConfigurableApplicationContext context) {
  for (SpringApplicationRunListener listener : this.listeners) {
    listener.contextLoaded(context);
  }
}

this.application指的是SpringApplication实例。这个方法会将SpringApplication中的ApplicationListener添加到ConfigurableApplicationContext 的applicationListeners中。如果applicationEventMulticaster不为null,也会同时放到applicationEventMulticaster的成员defaultRetriever.applicationListeners中。

@Override
public void contextLoaded(ConfigurableApplicationContext context) {
  // 将监听添加到应用上下文中
  for (ApplicationListener<?> listener : this.application.getListeners()) {
    if (listener instanceof ApplicationContextAware) {
      ((ApplicationContextAware) listener).setApplicationContext(context);
    }
    context.addApplicationListener(listener);
  }
  // 广播ApplicationPreparedEvent事件
  this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}

这里最终会如下所示,调用AnnotatedBeanDefinitionReader注册应用主类,也就是注册BeanDefinitionbeanDefinitionMap中并记录beanNamebeanDefinitionNames中。


⑤ listeners.contextLoaded

如下所示,这里实际上会触发EventPublishingRunListenercontextLoaded方法。

void contextLoaded(ConfigurableApplicationContext context) {
  for (SpringApplicationRunListener listener : this.listeners) {
    listener.contextLoaded(context);
  }
}

this.application指的是SpringApplication实例。这个方法会将SpringApplication中的ApplicationListener添加到ConfigurableApplicationContext 的applicationListeners中。如果applicationEventMulticaster不为null,也会同时放到applicationEventMulticaster的成员defaultRetriever.applicationListeners中。

@Override
public void contextLoaded(ConfigurableApplicationContext context) {
  // 将监听添加到应用上下文中
  for (ApplicationListener<?> listener : this.application.getListeners()) {
    if (listener instanceof ApplicationContextAware) {
      ((ApplicationContextAware) listener).setApplicationContext(context);
    }
    context.addApplicationListener(listener);
  }
  // 广播ApplicationPreparedEvent事件
  this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}

接下来会广播ApplicationPreparedEvent事件,有如下七个监听器对该事件感兴趣。

0 = {RestartApplicationListener@4405} 
1 = {CloudFoundryVcapEnvironmentPostProcessor@4406} 
2 = {ConfigFileApplicationListener@4407} 
3 = {LoggingApplicationListener@4409} 
4 = {BackgroundPreinitializer@4410} 
5 = {DelegatingApplicationListener@4412} 
6 = {DevToolsLogFactory$Listener@4414} 

到此,我们广播了ApplicationStartingEventApplicationEnvironmentPreparedEventApplicationContextInitializedEvent以及ApplicationPreparedEvent事件。



目录
相关文章
|
Java
springboot启动时执行
springboot启动时执行
73 0
|
8月前
|
XML Java 关系型数据库
Springboot启动时报错Property ‘mapperLocations‘ was not specified.
Springboot启动时报错Property ‘mapperLocations‘ was not specified.
316 2
|
9月前
|
Java 测试技术 数据库
SpringBoot启动时设置不加载数据库
SpringBoot启动时设置不加载数据库
454 0
|
9月前
|
存储 Java API
你了解SpringBoot启动时API相关信息是用什么数据结构存储的吗?(上篇)
你了解SpringBoot启动时API相关信息是用什么数据结构存储的吗?(上篇)
79 0
如何修改springboot项目启动时的默认图标?
如何修改springboot项目启动时的默认图标?
185 0
如何修改springboot项目启动时的默认图标?
|
9月前
|
Java Linux Windows
windows解决SpringBoot启动时:APPLICATION FAILED TO START
windows解决SpringBoot启动时:APPLICATION FAILED TO START
567 0
|
监控 安全 Java
SpringBoot启动时都做了哪些事(二)?
SpringBoot启动时都做了哪些事(二)?
90 0
|
Java
SpringBoot启动时都做了哪些事(一)?
SpringBoot启动时都做了哪些事(一)?
62 0
|
Java 容器
SpringBoot启动时都做了哪些事(四)?
SpringBoot启动时都做了哪些事(四)?
74 0
|
缓存 Java 数据库
Springboot项目启动时加载数据库数据到内存
Springboot项目启动时加载数据库数据到内存
173 0