Spring-ApplicationContext

spring源码版本 5.1.5.RELEASE

ApplicationContext

  • ApplicationContext和BeanFactory的关系?
  • Spring 中 ApplicationContext是怎么启动? 具体子类?
  • Spring web容器和Spring容器的关系?
  • SpringBoot中 ApplicationContext是怎么启动? 具体子类?

基础容器接口

image-20200115153732296

ApplicationContext

​ 先来看看ApplicationContext接口的定义

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
      MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
     ...
}

​ ApplicationContext和BeanFactory的关系是? 从下面的接口定义中,

似乎就是简单的继承,除了BeanFactory相关的接口 还新增了

  • ResourceLoader (资源加载)、 EnvironmentCapable (统称为资源管理)
  • ApplicationEventPublisher (事件发布)
  • MessageSource(国际化)

资源管理相关

Enviroment

@since 3.1 , 主要包含两部分 Profiles 和 properties

image-20200116162730961

spring容器的初始化流程

  • AbstractApplicationContext#getEnvironment
  • AbstractRefreshableWebApplicationContext#createEnvironment
  • AbstractRefreshableWebApplicationContext#customizePropertySources
  • MutablePropertySources#addLast

更多细节:

@see org.springframework.core.env.PropertySource

​ PropertySources

​ MutablePropertySources

@see org.springframework.core.env.PropertyResolver

​ PropertySourcesPropertyResolver 完成 占位符的解析和类型转换

​ 类型转换又是委托 ConversionService 完成的

@see org.springframework.context.support.PropertySourcesPlaceholderConfigurer

​ Bean 属性的占位符替换,需要注册 https://www.cnblogs.com/binarylei/p/8428211.html

扩展可参考: https://blog.csdn.net/zollty/article/details/86137826

在SpringBoot中是通过ConfigFileApplicationListener,加载相关

private void load(String location, String name, Profile profile,
      DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
   if (!StringUtils.hasText(name)) {
      for (PropertySourceLoader loader : this.propertySourceLoaders) {
         if (canLoadFileExtension(loader, location)) {
            load(loader, location, profile,
                  filterFactory.getDocumentFilter(profile), consumer);
            return;
         }
      }
   }
   Set<String> processed = new HashSet<>();
   for (PropertySourceLoader loader : this.propertySourceLoaders) {
      for (String fileExtension : loader.getFileExtensions()) {
         if (processed.add(fileExtension)) {
            loadForFileExtension(loader, location + name, "." + fileExtension,
                  profile, filterFactory, consumer);
         }
      }
   }
}

通过 org.springframework.boot.env.PropertySourceLoader 来加载

public interface PropertySourceLoader {

   /**
    * Returns the file extensions that the loader supports (excluding the '.').
    * @return the file extensions
    */
   String[] getFileExtensions();

   /**
    * Load the resource into one or more property sources. Implementations may either
    * return a list containing a single source, or in the case of a multi-document format
    * such as yaml a source for each document in the resource.
    * @param name the root name of the property source. If multiple documents are loaded
    * an additional suffix should be added to the name for each source loaded.
    * @param resource the resource to load
    * @return a list property sources
    * @throws IOException if the source cannot be loaded
    */
   List<PropertySource<?>> load(String name, Resource resource) throws IOException;
}

org.springframework.boot.env.PropertiesPropertySourceLoader

@Override
public List<PropertySource<?>> load(String name, Resource resource)
      throws IOException {
   Map<String, ?> properties = loadProperties(resource);
   if (properties.isEmpty()) {
      return Collections.emptyList();
   }
   return Collections
         .singletonList(new OriginTrackedMapPropertySource(name, properties));
}

最后生成 OriginTrackedMapPropertySource

ResourceLoader

Resource接口有很多实现类,而ResourceLoader接口用于实现不同的Resource加载策略,即将不同Resource实例的创建交给ResourceLoader来计算。

BeanDefinitionReader

使用Resource加载BeanDefinition

public interface BeanDefinitionReader {

   BeanDefinitionRegistry getRegistry();
  
   @Nullable
   ResourceLoader getResourceLoader();
  
   @Nullable
   ClassLoader getBeanClassLoader();
  
   BeanNameGenerator getBeanNameGenerator();

   //加载配置
   int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

   int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

     int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;

   int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;

}
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 1. 创建 XmlBeanDefinitionReader
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    // 2. 对 beanDefinitionReader 进行环境变量的设置
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    // 3. 对 beanDefinitionReader 进行设置,默认可以覆盖
    initBeanDefinitionReader(beanDefinitionReader);
    loadBeanDefinitions(beanDefinitionReader);
}

传统Spring中容器的启动流程

传统Spring启动方式

​ 传统的Spring容器的启动流程, 是依托于Servlet容器 , Servlet容器初始化完后,通过注册监听器, 实现回调

通常在 WEB-INF/web.xml 下会有如下配置

<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

在 org.springframework.web.context.ContextLoaderListener#contextInitialized 中

public void contextInitialized(ServletContextEvent event) {
   this.contextLoader = createContextLoader();
   if (this.contextLoader == null) {
      this.contextLoader = this;
   }
   this.contextLoader.initWebApplicationContext(event.getServletContext());
}

传统Spring启动流程

/**
 * Initialize Spring's web application context for the given servlet context,
 * using the application context provided at construction time, or creating a new one
 * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
 * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
 * @param servletContext current servlet context
 * @return the new WebApplicationContext
 * @see #ContextLoader(WebApplicationContext)
 * @see #CONTEXT_CLASS_PARAM
 * @see #CONFIG_LOCATION_PARAM
 */
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
   
      //重复初始化检查
      if (servletContext.getAttribute(
         WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
      throw new IllegalStateException(
            "Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!");
   }

   Log logger = LogFactory.getLog(ContextLoader.class);
   long startTime = System.currentTimeMillis();
   try {
      // Store context in local instance variable, to guarantee that
      // it is available on ServletContext shutdown.
      //创建容器
      if (this.context == null) {
         this.context = createWebApplicationContext(servletContext);
      }
      if (this.context instanceof ConfigurableWebApplicationContext) {
         ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
         if (!cwac.isActive()) {
            // The context has not yet been refreshed -> provide services such as
            // setting the parent context, setting the application context id, etc
            if (cwac.getParent() == null) {
               // The context instance was injected without an explicit parent ->
               // determine parent for root web application context, if any.
               ApplicationContext parent = loadParentContext(servletContext);
               cwac.setParent(parent);
            }
           //refresh操作
            configureAndRefreshWebApplicationContext(cwac, servletContext);
         }
      }
      servletContext.setAttribute(WebApplicationContext.
                                  ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

      ClassLoader ccl = Thread.currentThread().getContextClassLoader();
      if (ccl == ContextLoader.class.getClassLoader()) {
         currentContext = this.context;
      }
      else if (ccl != null) {
         currentContextPerThread.put(ccl, this.context);
      }
      return this.context;
   }
   catch (RuntimeException ex) {
      logger.error("Context initialization failed", ex);
      servletContext.setAttribute(WebApplicationContext.
                                  ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
      throw ex;
   }
   catch (Error err) {
      logger.error("Context initialization failed", err);
      servletContext.setAttribute(WebApplicationContext.
                                  ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
      throw err;
   }
}

ApplicationContext子类型

➡️ContextLoader#createWebApplicationContext(javax.servlet.ServletContext)

​ ➡️ ContextLoader#determineContextClass

​ ➡️ defaultStrategies.getProperty(WebApplicationContext.class.getName());

其中defaultStrategies的值在static中初始化了,DEFAULT_STRATEGIES_PATH的定义为ContextLoader.properties

static {
   // Load default strategy implementations from properties file.
   // This is currently strictly internal and not meant to be customized
   // by application developers.
   try {
      ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
      defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
   }
   catch (IOException ex) {
      throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
   }
}

在org/springframework/web/context/ContextLoader.properties路径下

# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

所以我们的子类型就是XmlWebApplicationContext

XmlWebApplicationContext相关类分析

在XmlWebApplicationContext的doc中

/
* @see #setNamespace
* @see #setConfigLocations
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see org.springframework.web.context.ContextLoader#initWebApplicationContext
* @see org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
*/
setConfigLocations

setConfigLocations 在web.xml中配置 , 用于获取目录

<!--Spring -->
<context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>
      classpath*:/config-spring/spring-*.xml
   </param-value>
</context-param>
XmlBeanDefinitionReader
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // Configure the bean definition reader with this context's
   // resource loading environment.
   beanDefinitionReader.setEnvironment(getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // Allow a subclass to provide custom initialization of the reader,
   // then proceed with actually loading the bean definitions.
   initBeanDefinitionReader(beanDefinitionReader);
   loadBeanDefinitions(beanDefinitionReader);
}

🌟ApplicationContext启动流程

关注refresh的部分,发现逻辑都在 AbstractApplicationContext

image-20200115164804998

org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext

AbstractRefreshableWebApplicationContext(ConfigurableWebApplicationContext,servletContext相关)

➡️AbstractRefreshableConfigApplicationContext(setConfigLocation)

​ ➡️AbstractRefreshableApplicationContext ( loadBeanDefinitions、refreshBeanFactory)

​ ➡️AbstractApplicationContext

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
   if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
      // The application context id is still set to its original default value
      // -> assign a more useful id based on available information
      String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
      if (idParam != null) {
         wac.setId(idParam);
      }
      else {
         // Generate default id...
         wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
               ObjectUtils.getDisplayString(sc.getContextPath()));
      }
   }

   wac.setServletContext(sc);
   String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
   if (configLocationParam != null) {
      wac.setConfigLocation(configLocationParam);
   }

   // The wac environment's #initPropertySources will be called in any case when the context
   // is refreshed; do it eagerly here to ensure servlet property sources are in place for
   // use in any post-processing or initialization that occurs below prior to #refresh
   ConfigurableEnvironment env = wac.getEnvironment();
   if (env instanceof ConfigurableWebEnvironment) {
      ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
   }

   customizeContext(sc, wac);
  //刷新
   wac.refresh();
}

refresh的定义在 org.springframework.context.ConfigurableApplicationContext#refresh

实现在org.springframework.context.support.AbstractApplicationContext#refresh

@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // 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);

         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         registerBeanPostProcessors(beanFactory);

         // 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) {
         // 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();
      }
   }
}
obtainFreshBeanFactory

刷新并获取beanFactory, 这里只是实例化

/**
 * Tell the subclass to refresh the internal bean factory.
 * @return the fresh BeanFactory instance
 * @see #refreshBeanFactory()
 * @see #getBeanFactory()
 */
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   refreshBeanFactory();
   return getBeanFactory();
}
refreshBeanFactory

org.springframework.context.support.AbstractApplicationContext#refreshBeanFactory

实现org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory

/**
 * This implementation performs an actual refresh of this context's underlying
 * bean factory, shutting down the previous bean factory (if any) and
 * initializing a fresh bean factory for the next phase of the context's lifecycle.
 */
@Override
protected final void refreshBeanFactory() throws BeansException {
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

所谓的refresh就是销毁bean close ,再重新创建和加载 (注意手动注册的bean 再refresh后,不会存在)

getBeanFactory

org.springframework.context.support.AbstractApplicationContext#getBeanFactory

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
      /** Bean factory for this context. */
      @Nullable
      private DefaultListableBeanFactory beanFactory;

      @Override
      public final ConfigurableListableBeanFactory getBeanFactory() {
         synchronized (this.beanFactoryMonitor) {
            if (this.beanFactory == null) {
               throw new IllegalStateException("BeanFactory not initialized or already closed - " +
                     "call 'refresh' before accessing beans via the ApplicationContext");
            }
            return this.beanFactory;
         }
      }
}

可以发现ApplicationContext和BeanFactory是组合的方式, 把BeanFactory相关的操作都委派给beanFactory

prepareBeanFactory

初始化BeanFactory, 配置相关回调(Aware), 内建依赖, 注册相关监听器,环境信息

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   // Tell the internal bean factory to use the context's class loader etc.
   beanFactory.setBeanClassLoader(getClassLoader());
   beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
   beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

   // Configure the bean factory with context callbacks.
   beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
   beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
   beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
   beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
   beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
   beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
   beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

   // BeanFactory interface not registered as resolvable type in a plain factory.
   // MessageSource registered (and found for autowiring) as a bean.
   beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
   beanFactory.registerResolvableDependency(ResourceLoader.class, this);
   beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
   beanFactory.registerResolvableDependency(ApplicationContext.class, this);

   // Register early post-processor for detecting inner beans as ApplicationListeners.
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

   // Detect a LoadTimeWeaver and prepare for weaving, if found.
   if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      // Set a temporary ClassLoader for type matching.
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }

   // Register default environment beans.
   if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
      beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
   }
   if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
      beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
   }
   if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
      beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
   }
}

属性相关 org.springframework.beans.PropertyEditorRegistrySupport#createDefaultEditors

postProcessBeanFactory

当前容器(application)的后置处理, 允许BeanFactory初始化后, bean实例化前的修改

/**
 * Modify the application context's internal bean factory after its standard
 * initialization. All bean definitions will have been loaded, but no beans
 * will have been instantiated yet. This allows for registering special
 * BeanPostProcessors etc in certain ApplicationContext implementations.
 * @param beanFactory the bean factory used by the application context
 */
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
invokeBeanFactoryPostProcessors

注册并调用BeanFactoryPostProcessor

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

   // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
   // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
   if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }
}

具体细节见: org.springframework.context.support.PostProcessorRegistrationDelegate#

invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanFactoryPostProcessor>)

registerBeanPostProcessors

注册BeanPostProcess

public static void registerBeanPostProcessors(
      ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

   String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

   // Register BeanPostProcessorChecker that logs an info message when
   // a bean is created during BeanPostProcessor instantiation, i.e. when
   // a bean is not eligible for getting processed by all BeanPostProcessors.
   int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
   beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

   // Separate between BeanPostProcessors that implement PriorityOrdered,
   // Ordered, and the rest.
   List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
   List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
   List<String> orderedPostProcessorNames = new ArrayList<>();
   List<String> nonOrderedPostProcessorNames = new ArrayList<>();
   for (String ppName : postProcessorNames) {
      if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
         BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
         priorityOrderedPostProcessors.add(pp);
         if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
         }
      }
      else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
         orderedPostProcessorNames.add(ppName);
      }
      else {
         nonOrderedPostProcessorNames.add(ppName);
      }
   }

   // First, register the BeanPostProcessors that implement PriorityOrdered.
   sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
   registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

   // Next, register the BeanPostProcessors that implement Ordered.
   List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
   for (String ppName : orderedPostProcessorNames) {
      BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
      orderedPostProcessors.add(pp);
      if (pp instanceof MergedBeanDefinitionPostProcessor) {
         internalPostProcessors.add(pp);
      }
   }
   sortPostProcessors(orderedPostProcessors, beanFactory);
   registerBeanPostProcessors(beanFactory, orderedPostProcessors);

   // Now, register all regular BeanPostProcessors.
   List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
   for (String ppName : nonOrderedPostProcessorNames) {
      BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
      nonOrderedPostProcessors.add(pp);
      if (pp instanceof MergedBeanDefinitionPostProcessor) {
         internalPostProcessors.add(pp);
      }
   }
   registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

   // Finally, re-register all internal BeanPostProcessors.
   sortPostProcessors(internalPostProcessors, beanFactory);
   registerBeanPostProcessors(beanFactory, internalPostProcessors);

   // Re-register post-processor for detecting inner beans as ApplicationListeners,
   // moving it to the end of the processor chain (for picking up proxies etc).
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
initMessageSource

国际化(Initialize message source for this context.)

initApplicationEventMulticaster

事件播放器, 配合listener使用(Initialize event multicaster for this context.)

this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);

image-20200116102230785

更多详情可以org.springframework.context.event.ApplicationEventMulticaster

实现细节org.springframework.context.event.SimpleApplicationEventMulticaster

#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)

tips: 默认实现是同步的, 可以设置执行器实现异步

onRefresh

模板方法 bean实例化前的操作(Initialize other special beans in specific context subclasses.) 专门留给子类初始化其它 bean 用,这是一个空的方法

registerListeners

注册监听器(Check for listener beans and register them.)

/**
 * Add beans that implement ApplicationListener as listeners.
 * Doesn't affect other listeners, which can be added without being beans.
 */
protected void registerListeners() {
   // Register statically specified listeners first.
   for (ApplicationListener<?> listener : getApplicationListeners()) {
      getApplicationEventMulticaster().addApplicationListener(listener);
   }

   // Do not initialize FactoryBeans here: We need to leave all regular beans
   // uninitialized to let post-processors apply to them!
   String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
   for (String listenerBeanName : listenerBeanNames) {
      getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
   }

   // Publish early application events now that we finally have a multicaster...
   Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
   this.earlyApplicationEvents = null;
   if (earlyEventsToProcess != null) {
      for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
         getApplicationEventMulticaster().multicastEvent(earlyEvent);
      }
   }
}

1.注册静态添加的listener

2.注册bean中的listener

3.发布一些早期事件 (发布事件 event,如果多播器懒加载,还没有初始化则将该事件先放到 earlyApplicationEvents 容器中)

finishBeanFactoryInitialization

实例化所有bean (Instantiate all remaining (non-lazy-init) singletons.)

更多: org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons

finishRefresh

发送相应事件 (publish corresponding event.)

/**
 * Finish the refresh of this context, invoking the LifecycleProcessor's
 * onRefresh() method and publishing the
 * {@link org.springframework.context.event.ContextRefreshedEvent}.
 */
protected void finishRefresh() {
   // Clear context-level resource caches (such as ASM metadata from scanning).
   clearResourceCaches();

   // Initialize lifecycle processor for this context.
   initLifecycleProcessor();

   // Propagate refresh to lifecycle processor first.
   getLifecycleProcessor().onRefresh();

   // Publish the final event.
   publishEvent(new ContextRefreshedEvent(this));

   // Participate in LiveBeansView MBean, if active.
   LiveBeansView.registerApplicationContext(this);
}

1.关闭资源缓存

2.初始化生命周期处理器

3.触发生命周期处理器

4.发布容器refresh事件

5.LiveBeansView #getSnapshotAsJson()

live bean的一个snapshot

static void registerApplicationContext(ConfigurableApplicationContext applicationContext) {
   String mbeanDomain = applicationContext.getEnvironment().getProperty(MBEAN_DOMAIN_PROPERTY_NAME);
   if (mbeanDomain != null) {
      synchronized (applicationContexts) {
         if (applicationContexts.isEmpty()) {
            try {
               MBeanServer server = ManagementFactory.getPlatformMBeanServer();
               applicationName = applicationContext.getApplicationName();
               server.registerMBean(new LiveBeansView(),
                     new ObjectName(mbeanDomain, MBEAN_APPLICATION_KEY, applicationName));
            }
            catch (Throwable ex) {
               throw new ApplicationContextException("Failed to register LiveBeansView MBean", ex);
            }
         }
         applicationContexts.add(applicationContext);
      }
   }
}

用console attach后

image-20200116111603497

Web容器

​ 当我们发起一次http请求时, 断点打在 AbstractApplicationContext#refresh 会发现又刷新了一次,

这是因为第一次刷新时spring容器(父容器), 而当第一次请求时,初始化springmvc容器 .并且刷新

  at org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:651)
  at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:602)
  at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:665)
  at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:521)
  at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:462)
  at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
  at javax.servlet.GenericServlet.init(GenericServlet.java:158)
  at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1230)
  - locked <0x1bf7> (a org.apache.catalina.core.StandardWrapper)
  at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1174)
  at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1066)
  at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5370)
  at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5668)
  - locked <0xc19> (a org.apache.catalina.core.StandardContext)
  at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)

根据堆栈信息找到调用链

org.springframework.web.servlet.FrameworkServlet#initServletBean

​ org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext

​ org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext

SpringBoot中的启动流程

启动方式

我们需要了解SpringBoot的启动方式SpringApplication#run(java.lang.Class<?>, java.lang.String…)即可运行.无需再通过servlet容器的回调

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
      String[] args) {
   return new SpringApplication(primarySources).run(args);
}

所以这里我们需要关注两部分, 构造函数,以及Run方法

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();
   setInitializers((Collection) getSpringFactoriesInstances(
         ApplicationContextInitializer.class));
   setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
   this.mainApplicationClass = deduceMainApplicationClass();
}

从run说起

/**
 * 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();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(
            SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(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, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }
   try {
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

启动流程

按照SpringApplicationRunListeners中的定义,可以稍微简单的分下步骤

  1. listeners.starting();
  2. listeners.environmentPrepared(environment);
  3. listeners.contextPrepared(context);
  4. listeners.contextLoaded(context);
  5. listeners.started(context);
  6. listeners.running(context);

运行失败的情况 会调用listeners.failed(context, exception);

server容器的启动方式

根据之前的分析,我们知道之前的传统spring的启动是通过servlet回调, 也就是先启动tomcat.

​ server容器 -> Spring容器

SpringBoot中是自主启动 , 启动流程是

​ SpringBoot应用 -> server容器

启动方式

在分析Application的启动流程中 AbstractApplicationContext中留了个#onRefresh的方法,

而在其子类中ServletWebServerApplicationContext重写了onRefresh的方法

@Override
protected void onRefresh() {
   super.onRefresh();
   try {
      createWebServer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
   }
}

在createWebServer中

private void createWebServer() {
   WebServer webServer = this.webServer;
   ServletContext servletContext = getServletContext();
   if (webServer == null && servletContext == null) {
      ServletWebServerFactory factory = getWebServerFactory();
      this.webServer = factory.getWebServer(getSelfInitializer());
   }
   else if (servletContext != null) {
      try {
         getSelfInitializer().onStartup(servletContext);
      }
      catch (ServletException ex) {
         throw new ApplicationContextException("Cannot initialize servlet context",
               ex);
      }
   }
   initPropertySources();
}

Servlet

org.springframework.boot.web.servlet.server.ServletWebServerFactory

image-20200116155347978

Reactive

org.springframework.boot.web.reactive.server.ReactiveWebServerFactory

image-20200116155728319


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 951488791@qq.com

文章标题:Spring-ApplicationContext

字数:4.1k

本文作者:zhengyumin

发布时间:2020-01-16, 21:04:45

最后更新:2020-10-13, 15:59:23

原始链接:http://zyumin.github.io/2020/01/16/Spring-ApplicationContext/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。