前言

内容主要参考自《Spring源码深度解析》一书,算是读书笔记或是原书的补充。进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情。

本文主要涉及书中第六章的部分,依照书中内容以及个人理解对Spring进行了注释,详见Github仓库:https://github.com/MrSorrow/spring-framework

前面的文章中,我们以 BeanFactory 接口以及它的实现类 XmlBeanFactory 为例分析了整个Spring加载配置文件、创建获取 bean 的过程。除此以外,Spring还有另一个接口 ApplicationContext,用于扩展 BeanFactory 中现有的功能。下面我们就仔细来研究一下这个接口及其实现类。

I. ApplicationContext使用

ApplicationContextBeanFactory 都是用于加载 bean 的,但是 ApplicationContext 提供了更多的功能,包含了 BeanFactory 的所有功能。通常情况下,在大多数应用中都使用的是 ApplicationContext,同时, XmlBeanFactory 在Spring3.1后已经被废弃,标记为过时。

查看 ClassPathXmlApplicationContext 类的继承结构,发现其也实现了 BeanFactory 接口。

Spring源码——容器扩展ApplicationContext-LMLPHP

首先来看一下两者在加载配置文件的写法差异:

  • 使用 BeanFactory 方式加载 XML

    XmlBeanFactory context = new XmlBeanFactory(new ClassPathResource("customTag-Test.xml"));
    
  • 使用 ApplicationContext 方式加载 XML

    ApplicationContext context = new ClassPathXmlApplicationContext("customTag-Test.xml");
    

同样,我们从 ClassPathXmlApplicationContext 出发,开始一步步深入探究。

/**
 * Create a new ClassPathXmlApplicationContext, loading the definitions
 * from the given XML file and automatically refreshing the context.
 * @param configLocation resource location
 * @throws BeansException if context creation failed
 */
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
   this(new String[] {configLocation}, true, null);
}

/**
 * Create a new ClassPathXmlApplicationContext with the given parent,
 * loading the definitions from the given XML files.
 * @param configLocations array of resource locations
 * @param refresh whether to automatically refresh the context,
 * loading all bean definitions and creating all singletons.
 * Alternatively, call refresh manually after further configuring the context.
 * @param parent the parent context
 * @throws BeansException if context creation failed
 * @see #refresh()
 */
public ClassPathXmlApplicationContext(
    String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
    throws BeansException {

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

查看其构造函数,configLocation 代表了配置文件的路径,是必传的参数。也可以设置多个路径,作为数组形式传入。对于XML的解析和功能实现则都在 refresh() 中实现。

II. 设置配置文件路径

ClassPathXmlApplicationContext 中支持多个配置文件以数组方式同时传入:

/**
 * 设置配置文件们的路径
 * Set the config locations for this application context.
 * <p>If not set, the implementation may use a default as appropriate.
 */
public void setConfigLocations(@Nullable String... locations) {
   if (locations != null) {
      Assert.noNullElements(locations, "Config locations must not be null");
      this.configLocations = new String[locations.length];
      for (int i = 0; i < locations.length; i++) {
         // 解析路径
         this.configLocations[i] = resolvePath(locations[i]).trim();
      }
   }
   else {
      this.configLocations = null;
   }
}

该函数通过 resolvePath() 解析给定的配置文件路径数组,如果路径包含特殊的符号,如 ${var},那么将会搜寻配置的环境变量并进行替换。我们在IDEA中测试一下:

Spring源码——容器扩展ApplicationContext-LMLPHP

我们在调用的时候,替换成:

ApplicationContext context = new ClassPathXmlApplicationContext("${config}");

IDEA中我们配置了环境变量 config=custtomTag-Test.xml,Spring会解析出该值。

/**
 * Resolve the given path, replacing placeholders with corresponding
 * environment property values if necessary. Applied to config locations.
 * @param path the original file path
 * @return the resolved file path
 * @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)
 */
protected String resolvePath(String path) {
   return getEnvironment().resolveRequiredPlaceholders(path);
}

resolvePath 通过 ConfigurableEnvironment 环境实例调用 resolveRequiredPlaceholders 方法进行解析,首先需要先获取 Environment,如果 AbstractApplicationContextthis.environment 属性为空,那么需要初始新建 StandardEnvironment 实例进行初始化。

/**
 * Return the {@code Environment} for this application context in configurable
 * form, allowing for further customization.
 * <p>If none specified, a default environment will be initialized via
 * {@link #createEnvironment()}.
 */
@Override
public ConfigurableEnvironment getEnvironment() {
   if (this.environment == null) {
      this.environment = createEnvironment();
   }
   return this.environment;
}

/**
 * Create and return a new {@link StandardEnvironment}.
 * <p>Subclasses may override this method in order to supply
 * a custom {@link ConfigurableEnvironment} implementation.
 */
protected ConfigurableEnvironment createEnvironment() {
    return new StandardEnvironment();
}

StandardEnvironment 继承自 AbstractEnvironmentAbstractEnvironment 中有一个 MutablePropertySources 类型的属性 propertySources,其中存放了我们配置的环境变量信息。读者可移自行debug进行验证。而Spring接着需要做的就是从中依据 key=config 来获取到 value=custtomTag-Test.xml

Spring源码——容器扩展ApplicationContext-LMLPHP

解析提取的工作委托给了 AbstractEnvironmentAbstractPropertyResolver 实例 propertyResolver

@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
   return this.propertyResolver.resolveRequiredPlaceholders(text);
}

@Override
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
    if (this.strictHelper == null) {
        this.strictHelper = createPlaceholderHelper(false);
    }
    return doResolvePlaceholders(text, this.strictHelper);
}

最终在 doResolvePlaceholders 中层层调用,这里就不继续深入了,可以自行debug:

protected String parseStringValue(
      String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {

   StringBuilder result = new StringBuilder(value);

   int startIndex = value.indexOf(this.placeholderPrefix);
   while (startIndex != -1) {
      int endIndex = findPlaceholderEndIndex(result, startIndex);
      if (endIndex != -1) {
         // 获取${}中的内容
         String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
         String originalPlaceholder = placeholder;
         if (!visitedPlaceholders.add(originalPlaceholder)) {
            throw new IllegalArgumentException(
                  "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
         }
         // Recursive invocation, parsing placeholders contained in the placeholder key.
         placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
         // Now obtain the value for the fully resolved key...
         // 根据key=config获取到value=custtomTag-Test.xml
         String propVal = placeholderResolver.resolvePlaceholder(placeholder);
         ....
      }
      else {
         startIndex = -1;
      }
   }

   return result.toString();
}

III. ApplicationContext扩展功能

解析完配置文件路径之后,保存在 ApplicationContext 成员变量 this.configLocations 中,然后便能够读取配置文件进行解析以及实现各种功能,这些都在 refresh() 中进行实现。

/**
 * 包含ApplicationContext所有功能
 * @throws BeansException
 * @throws IllegalStateException
 */
@Override
public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // 准备刷新的上下文环境,初始化前准备工作,对环境变量的验证
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      // 初始化BeanFactory,并进行XML文件读取
      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);

         // Invoke factory processors registered as beans in the context.
         // 激活各种BeanFactory处理器
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         // 注册 拦截Bean创建的 Bean处理器,这里只是注册,真正的调用是在getBean时候
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         // 为上下文初始化Message消息资源,即不同语言的消息体,国际化处理
         initMessageSource();

         // Initialize event multicaster for this context.
         // 初始化应用消息广播器,并放入"applicationEventMulticaster"bean中,与Spring的事件监听有关
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         // 留给子类来初始化其他bean
         onRefresh();

         // Check for listener beans and register them.
         // 在所有注册的bean中查找Listener bean,注册到消息广播器中
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         // 初始化剩下的单实例(非惰性的)
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         // 完成刷新过程,通知生命周期处理器 lifecycleProcessor 刷新过程,同时发出 ContextRefreshEvent 通知别人
         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();
      }
   }
}

方法中包含了本文需要讲的所有内容,每一个函数调用便是流程中的一个环节,我们先来配合注释整体把握一下:

  1. 初始化 Context 前的准备工作,例如对系统属性和环境变量进行准备以及验证等。

    在某种情况下,可能项目需要读取系统变量,而系统变量的设置可能会影响系统的正确性,那么 ClassPathXmlApplicationContext 为我们提供的这个准备函数 prepareRefresh() 就显得非常必要,它可以在Spring启动的时候提前对必须的变量进行存在性验证。

  2. 初始化 BeanFactory,并进行XML文件读取。

    BeanFactory 作为 ClassPathXmlApplicationContext 的成员变量,会在这里进行初始化(赋值),拥有了 BeanFactory 所有的功能。

  3. BeanFactory 进行各种功能填充。

    这一部分主要增加一些对Spring表达式语言的支持、一些属性编辑器的注册和其他一些扩展 beanFactory 琐碎细节。

  4. 子类重写 postProcessBeanFactory(beanFactory) 方法做额外处理。

    Spring的强大另一方面在于它的完美架构,开放式的架构让使用它的程序员容易的根据业务需要扩展已经存在的功能。这种开放式的设计在Spring中随处可见,如 postProcessBeanFactory(beanFactory)AbstractApplicationContext 中是一个实现方法,子类可以重写进行扩展。顺便提一句,postprocess 可以翻译为后期处理或后处理。

  5. 激活各种 BeanFactory 处理器。

  6. 注册拦截 bean 创建的 bean 处理器。

    标题注意断句!这里只是注册,真正调用是在 getBean() 的时候。

  7. Context 初始化 Message 源,即不同语言的消息体进行国际化处理。

  8. 初始化应用消息广播器,并放入 applicationEventMulticasterbean 中。

  9. 留给子类重写来初始化其他的 bean

  10. 在所有注册的 bean 中查找 listener bean,注册到消息广播器中。

  11. 初始化剩下的单实例(非惰性)。

  12. 完成刷新过程。

    完成 Context 的刷新,通知生命周期处理器 lifecycleProcessor 刷新过程,同时发出 ContextRefreshEvent 通知其他监听器做进一步处理。

下面,就对这12步骤一一进行细致分析。

IV. 环境准备

prepareRefresh 函数主要是做些准备工作,例如对系统属性及环境变量的初始化及验证。

/**
 * 做一些准备工作,例如对系统属性及环境变量的初始化及验证
 * Prepare this context for refreshing, setting its startup date and
 * active flag as well as performing any initialization of property sources.
 */
protected void prepareRefresh() {
   this.startupDate = System.currentTimeMillis();
   this.closed.set(false); // AtomicBoolean类型,并发
   this.active.set(true); // AtomicBoolean类型,并发

   if (logger.isDebugEnabled()) {
      logger.debug("Refreshing " + this);
   }

   // Initialize any placeholder property sources in the context environment
   // 留给子类覆盖实现
   initPropertySources();

   // 验证需要的属性文件是否都已经放入环境中
   // see ConfigurablePropertyResolver#setRequiredProperties
   getEnvironment().validateRequiredProperties();

   // Allow for the collection of early ApplicationEvents,
   // to be published once the multicaster is available...
   this.earlyApplicationEvents = new LinkedHashSet<>();
}

该函数感觉好像没什么用处,因为主要就是中文注释的两个函数,然而 initPropertySources 是一个空函数,没有任何逻辑,而 getEnvironment().validateRequiredProperties 也因为没有需要验证的属性而没做任何处理。其实不然。

initPropertySources 符合Spring开放式的结构设计,给用户最大扩展Spring能力。用户可以根据自身的需要重写该方法,并在该方法进行个性化的属性设置和处理;

validateRequiredProperties 是对属性进行验证,最终调用的其实是:

/**
 * 对属性进行验证
 */
@Override
public void validateRequiredProperties() {
   MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
   // this.requiredProperties可以通过getEnvironment().setRequiredProperties()方法设置
   for (String key : this.requiredProperties) {
      if (this.getProperty(key) == null) {
         ex.addMissingRequiredProperty(key);
      }
   }
   if (!ex.getMissingRequiredProperties().isEmpty()) {
      throw ex;
   }
}

这里验证的过程中用了Spring自定义的 MissingRequiredPropertiesException 异常类:

public class MissingRequiredPropertiesException extends IllegalStateException {

   private final Set<String> missingRequiredProperties = new LinkedHashSet<>();

   void addMissingRequiredProperty(String key) {
      this.missingRequiredProperties.add(key);
   }

   @Override
   public String getMessage() {
      return "The following properties were declared as required but could not be resolved: " + getMissingRequiredProperties();
   }

   public Set<String> getMissingRequiredProperties() {
      return this.missingRequiredProperties;
   }

}

该异常类中包含 missingRequiredProperties 属性,当 this.requiredProperties 中有属性没有时,会添加到其中。只要最后 missingRequiredProperties 属性中存在没有的,便会抛出 MissingRequiredPropertiesException 异常。this.requiredProperties 可以通过 getEnvironment().setRequiredProperties() 方法设置。

为了更好的理解,举个实际使用的例子。我们新建一个 ClassPathXmlApplicationContext 的子类,重写 initPropertySources() 方法,在其中添加需要验证的属性。

package guo.ping.ioc.validateproperties;

import org.springframework.beans.BeansException;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @description: 自定义扩展ClassPathXmlApplicationContext
 * @author: guoping wang
 * @date: 2018/10/3 12:34
 * @project: spring
 */
public class MyClassPathXmlAppllicationContext extends ClassPathXmlApplicationContext {

   public MyClassPathXmlAppllicationContext(String... configLocations) throws BeansException {
      super(configLocations);
   }

   /**
    * 重写方法添加需要验证的Properties
    */
   @Override
   protected void initPropertySources() {
      getEnvironment().setRequiredProperties("VAR");
   }
}

新建我们继承的类的测试类:

package guo.ping.ioc.bean;

import guo.ping.ioc.customtag.User;
import guo.ping.ioc.validateproperties.MyClassPathXmlAppllicationContext;
import org.junit.Test;
import org.springframework.context.ApplicationContext;

/**
 * @description: 测试验证属性
 * @author: guoping wang
 * @date: 2018/10/3 13:15
 * @project: spring
 */
public class ValidatePropertiesTest {

   @Test
   public void testValidateProperties() {
      ApplicationContext context = new MyClassPathXmlAppllicationContext("customTag-Test.xml");
      User user = (User) context.getBean("testUserBean");
   }
}

测试结果如下,如果系统并没有检测到对应 VAR 的环境变量,将抛出异常。

Spring源码——容器扩展ApplicationContext-LMLPHP

按照之前在IDEA设置环境变量的方法,设置了 VAR 环境变量,即可解决异常。

V. 加载BeanFactory

obtainFreshBeanFactory() 方法字面意思获取刷新的 BeanFactory,就是初始化 ApplicationContext 的成员变量 beanFactory,经过 obtainFreshBeanFactory() 方法,ApplicationContext 就会拥有 BeanFactory 的全部功能。

/**
 * 经过该函数后,ApplicationContext就拥有BeanFactory的功能
 * Tell the subclass to refresh the internal bean factory.
 * @return the fresh BeanFactory instance
 * @see #refreshBeanFactory()
 * @see #getBeanFactory()
 */
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   // 初始化BeanFactory,并进行XML文件读取,将得到的BeanFactory记录在当前实体的属性中
   refreshBeanFactory();
   // 返回当前实体的beanFactory属性
   return getBeanFactory();
}

真正的核心实现是在 refreshBeanFactory() 中,我们先看看 getBeanFactory()

@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;
   }
}

方法中主要是返回成员变量 this.beanFactory,我们可以推测出 this.beanFactoryrefreshBeanFactory() 中进行了赋值初始化。

/**
 * 初始化BeanFactory,并进行XML文件读取,将得到的BeanFactory记录在当前实体的属性中
 * 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,就是XmlBeanFactory继承的
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      // 为了序列化指定id,如果需要的话,让这个BeanFactory从id反序列化到BeanFactory对象
      beanFactory.setSerializationId(getId());
      // 定制beanFactory,设置相关属性,包括允许覆盖同名称的不同定义的对象、循环依赖
      // 可以被子类重新实现设置DefaultListableBeanFactory的任何配置
      customizeBeanFactory(beanFactory);
      // 初始化DocumentReader,并进行XML文件的读取和解析
      loadBeanDefinitions(beanFactory);
      // 使用全局变量记录BeanFactory类实例
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

主要的执行流程如下:

  1. 创建 DefaultListableBeanFactory

    没错,就是之前研究的 XmlBeanFactory 所继承的,DefaultListableBeanFactory 是容器的基础,ApplicationContext 想要实现容器的功能,必须执行实例化 DefaultListableBeanFactory 步骤。

  2. 指定序列化 ID。

  3. 定制 BeanFactory

  4. 加载 BeanDefinition

  5. 使用全局变量记录 BeanFactory 类实例。

    第五步正是我们推测一般,将创建的 beanFactory 赋值给成员变量,便于之后所有函数能够通过 getBeanFactory() 获得到 beanFactory 实例。

创建DefaultListableBeanFactory

创建该实例是通过 createBeanFactory() 方法实现的,调用 DefaultListableBeanFactory 的构造函数进行创建。

/**
 * Create an internal bean factory for this context.
 * Called for each {@link #refresh()} attempt.
 * <p>The default implementation creates a
 * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
 * with the {@linkplain #getInternalParentBeanFactory() internal bean factory} of this
 * context's parent as parent bean factory. Can be overridden in subclasses,
 * for example to customize DefaultListableBeanFactory's settings.
 * @return the bean factory for this context
 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowEagerClassLoading
 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
 */
protected DefaultListableBeanFactory createBeanFactory() {
   return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

指定序列化ID

BeanFactory 实现了序列化接口,BeanFactory 的序列化 id 通过获取 applicationContextid 属性进行设置,applicationContextid 属性设置是通过

/** Unique id for this context, if any. */
private String id = ObjectUtils.identityToString(this);

方法设置的。id 由类名称加上 @ 符号以及对象的 hashCode()

public static String identityToString(@Nullable Object obj) {
   if (obj == null) {
      return EMPTY_STRING;
   }
   return obj.getClass().getName() + "@" + getIdentityHexString(obj);
}

public static String getIdentityHexString(Object obj) {
    return Integer.toHexString(System.identityHashCode(obj));
}

如果需要的话,可以从 id 反序列化回 BeanFactory 对象。

定制BeanFactory

通过 customizeBeanFactory(beanFactory) 方法名便知道这里开始对 BeanFactory 进一步扩展,主要设置 beanFactory 能否允许覆盖同名称的不同定义的对象,循环依赖。

/**
 * 扩展beanFactory,添加相关属性,包括允许覆盖同名称的不同定义的对象、循环依赖
 * 可以被子类重新实现设置DefaultListableBeanFactory的任何配置
 * Customize the internal bean factory used by this context.
 * Called for each {@link #refresh()} attempt.
 * <p>The default implementation applies this context's
 * {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
 * and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
 * if specified. Can be overridden in subclasses to customize any of
 * {@link DefaultListableBeanFactory}'s settings.
 * @param beanFactory the newly created bean factory for this context
 * @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
 * @see DefaultListableBeanFactory#setAllowCircularReferences
 * @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
 * @see DefaultListableBeanFactory#setAllowEagerClassLoading
 */
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
   if (this.allowBeanDefinitionOverriding != null) {
      beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   if (this.allowCircularReferences != null) {
      beanFactory.setAllowCircularReferences(this.allowCircularReferences);
   }
}

对于允许覆盖和允许依赖的设置这里只判断了是否为空,一开始确实肯定是空,但我们哪里去设置呢?同样,方式之一可以子类重写 customizeBeanFactory 方法在其中进行设置:

public class MyClassPathXmlAppllicationContext extends ClassPathXmlApplicationContext {

   public MyClassPathXmlAppllicationContext(String... configLocations) throws BeansException {
      super(configLocations);
   }

   /**
    * 设置允许覆盖和依赖
    * @param beanFactory the newly created bean factory for this context
    */
   @Override
   protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
      setAllowBeanDefinitionOverriding(true);
      setAllowCircularReferences(true);
      super.customizeBeanFactory(beanFactory);
   }
}

测试结果:
Spring源码——容器扩展ApplicationContext-LMLPHP
书中还有关于 @Qualifier@AutoWire 注解的设置,然而至少在新版Spring5去掉了相关设置代码了。先留给坑位,以后看到回来补充…

加载BeanDefinition

之前我们已经获得了 DefaultListableBeanFactory 的实例,Spring容器已经创建好了,现在需要读取XML配置文件。按照之前的套路,需要利用 XmlBeanDefinitonReader 来读取,所以,在 ApplicationContext 中也创建了该类对象来进行读取XML并进行配置文件的加载。

/**
 * 加载BeanDefinition
 * Loads the bean definitions via an XmlBeanDefinitionReader.
 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
 * @see #initBeanDefinitionReader
 * @see #loadBeanDefinitions
 */
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // 为指定的beanFactory创建XmlBeanDefinitionReader,用于读取XML
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

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

   // 对BeanDefinitionReader进行设置,默认为空实现,可以子类重新实现对BeanDefinitionReader定制
   initBeanDefinitionReader(beanDefinitionReader);
   // 调用重载方法加载BeanDefinition
   loadBeanDefinitions(beanDefinitionReader);
}

方法内容还是易于理解的,中间对于 beanDefinitionReader 进行了一些属性的设置,设置 ResourceLoader 时,传入了 this 对象,因为本身 ClassPathXmlApplicationContext 实现了 ResourceLoader 接口。至于 EntityResolver 的作用已经在第一篇文章就已经提及了。

当然,如果用户需要对 beanDefinitionReader 特殊的设置,同样Spring留了扩展的空方法 initBeanDefinitionReader(beanDefinitionReader),子类依然可以重写:

public class MyClassPathXmlAppllicationContext extends ClassPathXmlApplicationContext {
   ....

   @Override
   protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
      // 扩展
      super.initBeanDefinitionReader(reader);
   }
}

准备好 beanDefinitionReader 后,可以正式加载 BeanDefinition,从 loadBeanDefinitions(beanDefinitionReader) 方法看,有点眼熟,确实,之前 XmlBeanFactory 加载 BeanDefinition 时,也是相同的方法名。

/**
 * 利用XmlBeanDefinitionReader的loadBeanDefinitions方法读取XML配置文件
 * Load the bean definitions with the given XmlBeanDefinitionReader.
 * <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
 * method; hence this method is just supposed to load and/or register bean definitions.
 * @param reader the XmlBeanDefinitionReader to use
 * @throws BeansException in case of bean registration errors
 * @throws IOException if the required XML document isn't found
 * @see #refreshBeanFactory
 * @see #getConfigLocations
 * @see #getResources
 * @see #getResourcePatternResolver
 */
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}

果然,其中调用了 XmlBeanDefinitionReaderloadBeanDefinitions(configLocations) 方法,关于该方法,其实是 loadBeanDefinitions(Resource resource) 的重载方法,最终仍然会将 String 类型的 configLocations 参数包装成 Resource 类型。这样我们就回到 XmlBeanFactory 加载 BeanDefinitions 的分析步骤了。

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
   ResourceLoader resourceLoader = getResourceLoader();
   if (resourceLoader == null) {
      throw new BeanDefinitionStoreException(
            "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
   }

   if (resourceLoader instanceof ResourcePatternResolver) {
      // Resource pattern matching available.
      try {
         Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
         // 回到XmlBeanFactory加载BeanDefinitions的分析步骤了
         int loadCount = loadBeanDefinitions(resources);
         if (actualResources != null) {
            for (Resource resource : resources) {
               actualResources.add(resource);
            }
         }
         if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
         }
         return loadCount;
      }
      catch (IOException ex) {
         throw new BeanDefinitionStoreException(
               "Could not resolve bean definition resource pattern [" + location + "]", ex);
      }
   }
   else {
      // Can only load single resources by absolute URL.
      Resource resource = resourceLoader.getResource(location);
      int loadCount = loadBeanDefinitions(resource);
      if (actualResources != null) {
         actualResources.add(resource);
      }
      if (logger.isDebugEnabled()) {
         logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
      }
      return loadCount;
   }
}

VI. beanFactory功能扩展

进入 prepareBeanFactory(beanFactory) 方法前,Spring已经完成了配置文件的解析,包括默认标签和自定义标签的解析,这些都是靠之前的 DefaultListableBeanFactory 完成的。而 ApplicationContext 在这之上的扩展由此展开。

/**
 * Configure the factory's standard context characteristics,
 * such as the context's ClassLoader and post-processors.
 * @param beanFactory the BeanFactory to configure
 */
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   // Tell the internal bean factory to use the context's class loader etc.
   // 设置beanFactory的classLoader为当前context的classLoader
   beanFactory.setBeanClassLoader(getClassLoader());

   // 设置beanFactory的表达式语言处理器,Spring3增加了SPEL表达式语言的支持
   // 默认可以使用#{bean.xxx}的形式来调用相关属性值
   beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

   // 为beanFactory增加一个默认的propertyEditor,主要是对bean的属性等设置管理的一个工具(属性编辑器的支持)
   beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

   // 添加BeanPostProcessor
   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.
   // 添加BeanPostProcessor实现:ApplicationListenerDetector,它是用来将实现了ApplicationListener接口的Bean添加到容器的监听器列表
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

   // Detect a LoadTimeWeaver and prepare for weaving, if found.
   // 增加对AspectJ的支持
   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()));
   }

   // 以单例模式注册默认的系统环境bean
   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());
   }
}

上面的函数对已有的 beanFactory 作了如下扩展:

  • 设置 beanFactoryclassLoader
  • 增加对 SPEL 语言的支持
  • 增加对属性编辑器的支持
  • 增加对一些内置类的信息注入,如 EnvironmentAwareMessageSourceAware
  • 设置了依赖功能可忽略的接口
  • 设置几个自动装配的特殊规则
  • 增加 AspectJ 的支持(后面AOP单独研究)
  • 将相关环境变量及属性注册以单例模式注册

设置classLoader

设置 beanFactoryclassLoader 为当前 ContextclassLoader

@Override
public void setBeanClassLoader(@Nullable ClassLoader beanClassLoader) {
   this.beanClassLoader = (beanClassLoader != null ? beanClassLoader : ClassUtils.getDefaultClassLoader());
}

增加SPEL语言的支持

Spring表达式语言全称为“Spring Expression Language”,缩写为“SpEL” 。SpEL是单独模块,只依赖于core模块,不依赖于其他模块,可以单独使用。

Spring源码——容器扩展ApplicationContext-LMLPHP

关于SpEL具体使用,移步参考阅读。SpEL使用 #{···} 作为界定符,所有在大括号内的字符串被认为是SpEL。SpEL的解析正是通过 setBeanExpressionResolver(BeanExpressionResolver resolver) 注册的语言解析器进行的。

// 设置beanFactory的表达式语言处理器,Spring3增加了SPEL表达式语言的支持// 默认可以使用#{bean.xxx}的形式来调用相关属性值beanFactory.setBeanExpressionResolver
10-06 10:20