跳至主要內容

Spring IOC

blacklad大约 6 分钟SpringSpring

Spring IOC

IOC 容器是 Spring 的一大特性,在开发过程中也经常会用到容器提供的一些功能,所以便对容器相关的源码进行了阅读了解,为了更好的梳理 Spring IOC 容器相关逻辑,主要以经典的 ClassPathXmlApplicationContext容器做分析,常用的注解类容器主要原理与之相似,可参考阅读。

tips: 阅读前,你需要对 Spring 有一些了解。

1 容器初始化

首先写一个 xml 配置文件,CatDog 为容器中的两个bean。

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd ">

    <bean id="cat" class="com.blacklad.test.spring.Cat" >
    </bean>

    <bean id="dog" class="com.blacklad.test.spring.Dog" >
    </bean>
    
</beans>

通过 xml 创建一个 ClassPathXmlApplicationContext 容器。

public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-ioc.xml");
    Cat cat = ctx.getBean("cat", Cat.class);
    Dog dog = ctx.getBean("dog", Dog.class);
}

2 启动流程

在创建ClassPathXmlApplicationContext对象时,Spring 就对容器进行了初始化。并将 xml 文件的路径保存下来,然后调用 refresh 方法。

// org.springframework.context.support.ClassPathXmlApplicationContext

public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
    this(configLocations, true, null);
}

public ClassPathXmlApplicationContext(
      String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
      throws BeansException {

   super(parent);
   setConfigLocations(configLocations);
   if (refresh) {
      refresh();
   }
}

refresh方法时整个 Spring 容器启动的核心,其代码也是写在了抽象类AbstractApplicationContext中,所有实现这个类的容器都会调用这个方法刷新(初始化),方法中每一步都是通过调用一个函数实现,其中一部分是抽象方法,交给子类实现。(用到了模板方法的设计模式),整个步骤清晰明了。

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

3 refresh

方法内首先会加同步锁,保证线程安全。

3.1 prepareRefresh

准备刷新阶段,设置启动时间和刷新开始标志。

3.2 obtainFreshBeanFactory

通过子类实现refreshBeanFactory方法刷新beanFactory。

// org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory
/**
 * 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 中首先会 create 一个 beanFactory -> DefaultListableBeanFactory,然后对其进行初始化,并通过 loadBeanDefinitions() 加载所有的bean配置作为beanDefinition对象放入beanDefinitionMap中。

// org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
@Override
protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory);
        loadBeanDefinitions(beanFactory);
        this.beanFactory = beanFactory;
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

3.3 prepareBeanFactory

对 BeanFactory 做一些预备的配置,如 ClassLoaderfactory 相关的 BeanPostProcessor 和一些默认的bean。

3.4 postProcessBeanFactory

在工厂初始化后,对 BeanFactory 做一些修改。

/**
 * 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) {
}

3.5 invokeBeanFactoryPostProcessors

调用所有的 BeanFactoryPostProcessors

// org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
/**
 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
 * respecting explicit order if given.
 * <p>Must be called before singleton instantiation.
 */
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()));
   }
}

invokeBeanFactoryPostProcessors 内会按照优先级依次处理。

  1. 首先会优先处理实现了BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor的类中的方法postProcessBeanDefinitionRegistry()

  2. 然后依调用实现了BeanFactoryPostProcessor接口的类中的postProcessBeanFactory方法。

3.6 registerBeanPostProcessors

按照优先级注册所有实现了 BeanPostProcessor 的类。

/**
 * Instantiate and register all BeanPostProcessor beans,
 * respecting explicit order if given.
 * <p>Must be called before any instantiation of application beans.
 */
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
   PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

3.7 initMessageSource

初始化国际化相关配置。

3.8 initApplicationEventMulticaster

初始化事件广播器,会创建一个SimpleApplicationEventMulticaster对象放入beanFactory容器中。

3.9 onRefresh

模板方法,供子类实现去做一些额外的刷新功能,如初始化一些特殊的 beans

/**
 * Template method which can be overridden to add context-specific refresh work.
 * Called on initialization of special beans, before instantiation of singletons.
 * <p>This implementation is empty.
 * @throws BeansException in case of errors
 * @see #refresh()
 */
protected void onRefresh() throws BeansException {
   // For subclasses: do nothing by default.
}

3.10 registerListeners

将事件监听器(即实现了 ApplicationListener 接口的类)注册到事件广播器中。

3.11 finishBeanFactoryInitialization

完成对 beanfactory 的初始化, 同时对BeanFactory中的 singleton bean 进行初始化。

/**
 * Finish the initialization of this context's bean factory,
 * initializing all remaining singleton beans.
 */
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   // Initialize conversion service for this context.
   if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
         beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
      beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
   }

   // Register a default embedded value resolver if no bean post-processor
   // (such as a PropertyPlaceholderConfigurer bean) registered any before:
   // at this point, primarily for resolution in annotation attribute values.
   if (!beanFactory.hasEmbeddedValueResolver()) {
      beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
   }

   // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
   String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
   for (String weaverAwareName : weaverAwareNames) {
      getBean(weaverAwareName);
   }

   // Stop using the temporary ClassLoader for type matching.
   beanFactory.setTempClassLoader(null);

   // Allow for caching all bean definition metadata, not expecting further changes.
   beanFactory.freezeConfiguration();

   // Instantiate all remaining (non-lazy-init) singletons.
   beanFactory.preInstantiateSingletons();
}

preInstantiateSingletons 中会遍历所有的 beanDefinition 信息,找到 非抽象、单例且非懒加载 的类通过调用 getBean 方法进行初始化 bean

@Override
public void preInstantiateSingletons() throws BeansException {
   if (logger.isTraceEnabled()) {
      logger.trace("Pre-instantiating singletons in " + this);
   }

   // Iterate over a copy to allow for init methods which in turn register new bean definitions.
   // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
   List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

   // Trigger initialization of all non-lazy singleton beans...
   for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
         if (isFactoryBean(beanName)) {
            Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
            if (bean instanceof FactoryBean) {
               FactoryBean<?> factory = (FactoryBean<?>) bean;
               boolean isEagerInit;
               if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                  isEagerInit = AccessController.doPrivileged(
                        (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                        getAccessControlContext());
               }
               else {
                  isEagerInit = (factory instanceof SmartFactoryBean &&
                        ((SmartFactoryBean<?>) factory).isEagerInit());
               }
               if (isEagerInit) {
                  getBean(beanName);
               }
            }
         }
         else {
            getBean(beanName);
         }
      }
   }

   // Trigger post-initialization callback for all applicable beans...
   for (String beanName : beanNames) {
      Object singletonInstance = getSingleton(beanName);
      if (singletonInstance instanceof SmartInitializingSingleton) {
         SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
         if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
               smartSingleton.afterSingletonsInstantiated();
               return null;
            }, getAccessControlContext());
         }
         else {
            smartSingleton.afterSingletonsInstantiated();
         }
      }
   }
}

3.12 finishRefresh

完成 context 的刷新,清理资源,初始化生命周期处理器,发布 ContextRefreshedEvent 事件。

/**
 * 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);
}

4 总结

执行完 refresh 方法,Spring 容器的启动流程就结束了,这里大概介绍了容器启动过程中的一些主要的流程框架,一些细节没有具体去分析,后续会挑一些重要的部分进行分析总结。

上次编辑于:
贡献者: blacklad