美文网首页技术文技术干货程序员
SpringIOC源码阅读—解析xml创建BeanDefinit

SpringIOC源码阅读—解析xml创建BeanDefinit

作者: 激情的狼王 | 来源:发表于2017-11-21 12:16 被阅读0次

上篇文章中Spring-IOC源码---创建BeanFactroy我们可以知道Spring初始化的主体就是refresh()方法,并且找到了创建BeanFactory的地方,也就是obtainFreshBeanFactory,该方法先判断是否有工厂,最终new了一个BeanFactory的实例DefaultListableBeanFactory

protected final void refreshBeanFactory() throws BeansException {
        //判断工厂是否存在
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            //创建Bean工厂
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            //解析配置文件并加载bean
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

本篇继续分析bean工厂是怎么将xml等配置文件里的bean实例初始化进工厂的,也就是怎么生成BeanDefinition的Map的,也就是上面注释的loadBeanDefinitions
断点跟进loadBeanDefinitions方法

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(this.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);
    }

首先new了一个XmlBeanDefinitionReader(这里思考为什么构造方法传入beanFactory?),我们都知道Reader是用来读取文件的,后续是init步骤,重点看最后一行的loadBeanDefinitions,可以观察到AbstractXmlApplicationContext里的loadBeanDefinitions是重载的(以后有时间细聊),我们跟进去看

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

这里可以看到reader.loadBeanDefinitions(configResources);reader加载bean通过configResources,可以跟出来configResources就是ClassPathXmlApplicationContext构造方法的setConfigLocations(configLocations);

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

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

到这里我们可以猜到后续的步骤了(翻阅千山万水后找到了如下源码):那就是解析xml

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

然后就是注册bean:

public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        //注册bean,唯一的name
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        //如果bean有别名,循环注册
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

大家可以翻译过来的注释,bean在注册的时候是存在别名的,需要都注册进来。

其中解析完xml获取到bean的id或name后,实例化这个bean的过程就是通过反射实现的,有兴趣可以自己读,本篇不做详细描述,后续我们会再聊的。

梳理一下从创建DefaultListableBeanFactory到加载完Map<String,BeanDefinition>的大致步骤:

DefaultListableBeanFactory—>
AbstractXmlApplicationContext—>
AbstractBeanDefinitionReader—>
XmlBeanDefinitionReader—>
DefaultBeanDefinitionDocumentReader—>
DefaultListableBeanFactory

很显然兜了一圈,包含了十几个方法跟进,思考:为什么这么做呢?直接干不好吗?

到这里我们基本聊完了IOC的bean工厂和bean实例的创建过程,这两个过程中有很多小细节供大家慢慢查阅和实践,一篇文章毕竟不能描述的太细太深入,那样的话既没太大的价值也容易催眠,所以仅做大致的脉络研究。

相关文章

网友评论

    本文标题:SpringIOC源码阅读—解析xml创建BeanDefinit

    本文链接:https://www.haomeiwen.com/subject/elgivxtx.html