15--IoC容器启动过程简析及XmlBeanFactory初始化

上篇分析了Spring对资源文件的加载过程,接下来我们就要开始分析Spring的IoC容器了(基于XmlBeanFactory)。

1.IoC容器启动过程简析

注意:以BeanFactory为基础的IoC容器在启动完成之后,并不会立刻实例化配置文件中的bean,首次实例化发生在我们第一次向容器索取的过程中。如果IoC容器这个概念生涩难懂、或者让人觉得有些深奥的话,那么就理解为一个类的实例化即可,只不过这个类的实例化过程,比较复杂而已!

在上一篇也介绍了IoC容器的启动过程为:加载资源文件、解析资源文件、注册BeanDefinition,我们再来看一个更为详细的流程图(该流程图只列举了比较重要的步骤)。


image.png

总而言之,就是将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的继承关系依次调用各个父类的构造方法:


flowchat
st=>start: 开始
e=>end: 结束
op1=>operation: 初始化AbstractBeanFactory
op2=>operation: 初始化AbstractAutowireCapableBeanFactory
op3=>operation: 初始化DefaultListableBeanFactory
op4=>operation: 初始化XmlBeanFactory 
st->op1->op2->op3->op4->e

该过程初始化的信息很多,我们选择其中比较重要的几点做下介绍。

  • 忽略指定接口的自动装配功能
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);
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容