美文网首页技术文技术干货程序员
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