天天看點

15--IoC容器啟動過程簡析及XmlBeanFactory初始化

上篇分析了Spring對資源檔案的加載過程,接下來我們就要開始分析Spring的IoC容器了(基于XmlBeanFactory)。

1.IoC容器啟動過程簡析

注意:以BeanFactory為基礎的IoC容器在啟動完成之後,并不會立刻執行個體化配置檔案中的bean,首次執行個體化發生在我們第一次向容器索取的過程中。如果IoC容器這個概念生澀難懂、或者讓人覺得有些深奧的話,那麼就了解為一個類的執行個體化即可,隻不過這個類的執行個體化過程,比較複雜而已!

在上一篇也介紹了IoC容器的啟動過程為:加載資源檔案、解析資源檔案、注冊BeanDefinition,我們再來看一個更為詳細的流程圖(該流程圖隻列舉了比較重要的步驟)。

總而言之,就是将xml檔案轉換為SpringIoC容器的内部表示。

2. XmlBeanFactory初始化

打開我們之前複習Spring知識點的測試類,以

xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("v2/day01.xml"));

為切入點。打開XmlBeanFactory類。

public class XmlBeanFactory extends DefaultListableBeanFactory {

	// 執行個體化XmlBeanDefinitionReader對象
	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


	/**
	 * 通過指定Resource對象建立XmlBeanFactory執行個體
	 */
	public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}

	/**
	 * 通過指定Resource對象和父BeanFactory建立XmlBeanFactory執行個體
	 */
	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		// 依次向上執行個體化父類構造器
		super(parentBeanFactory);
		// 解析xml配置檔案,将其轉換為IoC容器的内部表示
		this.reader.loadBeanDefinitions(resource);
	}
}
           

先來分析加載bean之前的準備工作,XmlBeanFactory父類初始化和XmlBeanDefinitionReader初始化。

2.1 父類初始化

通過XmlBeanFactory的繼承關系依次調用各個父類的構造方法:

該過程初始化的資訊很多,我們選擇其中比較重要的幾點做下介紹。

  • 忽略指定接口的自動裝配功能
public AbstractAutowireCapableBeanFactory() {
    super();
    // 忽略指定接口的自動裝配功能
    ignoreDependencyInterface(BeanNameAware.class);
    ignoreDependencyInterface(BeanFactoryAware.class);
    ignoreDependencyInterface(BeanClassLoaderAware.class);
}
           

忽略指定接口的自動裝配功能:如ClassA引用了ClassB,那麼當Spring在擷取ClassA的執行個體時,如果發現ClassB還沒有被初始化,那麼Spring會自動初始化ClassB。但是如果ClassB實作了BeanNameAware接口的話,則Spring不會自動初始化ClassB,這就是忽略指定接口的自動裝配。

2.2 初始化XmlBeanDefinitionReader

将xml檔案中的配置轉換為IoC内部的表示就是由XmlBeanDefinitionReader來完成的。XmlBeanDefinitionReader繼承了AbstractBeanDefinitionReader類,我們來其初始化都完成了哪些操作。

protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;

    // Determine ResourceLoader to use.
    // 1、确定ResourceLoader使用。
    if (this.registry instanceof ResourceLoader) {
        this.resourceLoader = (ResourceLoader) this.registry;
    }
    else {
        this.resourceLoader = new PathMatchingResourcePatternResolver();
    }

    // Inherit Environment if possible
    // 2、如果環境可繼承則繼承registry的環境,否則重新建立環境
    if (this.registry instanceof EnvironmentCapable) {
        this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
    }
    else {
        this.environment = new StandardEnvironment();
    }
}
           
  • 1、确定ResourceLoader使用。該方法的核心就是确定目前使用的類加載器
public PathMatchingResourcePatternResolver() {
    this.resourceLoader = new DefaultResourceLoader();
}
           
public DefaultResourceLoader() {
    this.classLoader = ClassUtils.getDefaultClassLoader();
}
           
public static ClassLoader getDefaultClassLoader() {
    ClassLoader cl = null;
    try {
        //優先擷取線程上下文類加載器
        cl = Thread.currentThread().getContextClassLoader();
    }
    catch (Throwable ex) {
        // Cannot access thread context ClassLoader - falling back...
    }
    if (cl == null) {
        // No thread context class loader -> use class loader of this class.
        // 擷取目前類的類加載器
        cl = ClassUtils.class.getClassLoader();
        if (cl == null) {
            // getClassLoader() returning null indicates the bootstrap ClassLoader
            try {
                //擷取SystemClassLoader
                cl = ClassLoader.getSystemClassLoader();
            }
            catch (Throwable ex) {
                // Cannot access system ClassLoader - oh well, maybe the caller can live with null...
            }
        }
    }
    return cl;
}
           
  • 2、如果環境可繼承則繼承registry的環境,否則重新建立環境

    系統環境包括了系統環境屬性(主機變量資訊)、JVM系統環境屬性(JDK版本,JDK目錄等)、預設激活節點、屬性解析器等。

StandardEnvironment初始化

public class StandardEnvironment extends AbstractEnvironment {

	/** 系統環境屬性 System environment property source name: {@value}. */
	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

	/** JVM系統環境屬性 JVM system properties property source name: {@value}. */
	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";


	/**
	 * Customize the set of property sources with those appropriate for any standard
	 * Java environment:
	 * <ul>
	 * <li>{@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME}
	 * <li>{@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}
	 * </ul>
	 * <p>Properties present in {@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME} will
	 * take precedence over those in {@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}.
	 * @see AbstractEnvironment#customizePropertySources(MutablePropertySources)
	 * @see #getSystemProperties()
	 * @see #getSystemEnvironment()
	 */
	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		// 主要通過System類來擷取資訊

		// 擷取系統環境屬性并加入到propertySources中
		propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		// 擷取JVM系統環境屬性并加入到propertySources中
		propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
	}

}
           

AbstractEnvironment初始化

private final MutablePropertySources propertySources = new MutablePropertySources();
private final ConfigurablePropertyResolver propertyResolver = new PropertySourcesPropertyResolver(this.propertySources);
public AbstractEnvironment() {
	customizePropertySources(this.propertySources);
}
           

繼續閱讀