zoukankan      html  css  js  c++  java
  • 从零开始学spring源码之xml解析(一):入门

        谈到spring,首先想到的肯定是ioc,DI依赖注入,aop,但是其实很多人只是知道这些是spring核心概念,甚至不知道这些代表了什么意思,,作为一个java程序员,怎么能说自己对号称改变了java生态的spring不了解呢。

        首先说一下spring做了啥,他将我们会频繁用到的javaBean交给spring的容器管理,在bean创建的不同阶段,可以在不同的容器中找到,说到底这些容器就是map缓存,有了spring我们就有了管家,可以过过当老爷的瘾,再也不用担心对象啥时候创建,初始化,销毁的问题了,用就完事了。

        说了这么多废话,还是直接撸源码实在,首先看spring源码入口:

    public void refresh() throws BeansException, IllegalStateException {
            synchronized (this.startupShutdownMonitor) {
                //为容器初始化做准备
                // Prepare this context for refreshing.
                prepareRefresh();
    
                /*
    
                  1、创建BeanFactory对象
                * 2、xml解析
                *     传统标签解析:bean、import等
                *     自定义标签解析 如:<context:component-scan base-package="com.xiangxue.jack"/>
                *     自定义标签解析流程:
                *         a、根据当前解析标签的头信息找到对应的namespaceUri
                *         b、加载spring所以jar中的spring.handlers文件。并建立映射关系
                *         c、根据namespaceUri从映射关系中找到对应的实现了NamespaceHandler接口的类
                *         d、调用类的init方法,init方法是注册了各种自定义标签的解析类
                *         e、根据namespaceUri找到对应的解析类,然后调用paser方法完成标签解析
                *
                * 3、把解析出来的xml标签封装成BeanDefinition对象
                * */
                // Tell the subclass to refresh the internal bean factory.
                ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
                // Prepare the bean factory for use in this context.
                prepareBeanFactory(beanFactory);
    
                try {
                    // Allows post-processing of the bean factory in context subclasses.
                    postProcessBeanFactory(beanFactory);
    
                    // Invoke factory processors registered as beans in the context.
                    invokeBeanFactoryPostProcessors(beanFactory);
    
                    // Register bean processors that intercept bean creation.
                    registerBeanPostProcessors(beanFactory);
    
                    // Initialize message source for this context.
                    initMessageSource();
    
                    // Initialize event multicaster for this context.
                    initApplicationEventMulticaster();
    
                    // Initialize other special beans in specific context subclasses.
                    onRefresh();
    
                    // Check for listener beans and register them.
                    registerListeners();
    
                    // Instantiate all remaining (non-lazy-init) singletons.
                    finishBeanFactoryInitialization(beanFactory);
    
                    // Last step: publish corresponding event.
                    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();
                }
            }
        }

        这一个方法,包含了所有spring的操作,spring的代码果然是美如画,加上trycatch才十几行代码就改变了java的生态。    

    prepareRefresh();首先看看这个方法,有兴趣的可以自己看看,就是刷新上下文,设置启动日期之类的。

    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();我们主要看看这一行代码,进去看看

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
            //核心方法
            refreshBeanFactory();
            return getBeanFactory();
        }

    好像还看不到啥关键的,再看看refreshBeanFactory();

    protected final void refreshBeanFactory() throws BeansException {
    
            //如果BeanFactory不为空,则清除BeanFactory和里面的实例
            if (hasBeanFactory()) {
                destroyBeans();
                closeBeanFactory();
            }
            try {
                //创建DefaultListableBeanFactory
                DefaultListableBeanFactory beanFactory = createBeanFactory();
                beanFactory.setSerializationId(getId());
    
                //设置是否可以循环依赖 allowCircularReferences
                //是否允许使用相同名称重新注册不同的bean实现.
                customizeBeanFactory(beanFactory);
    
                //解析xml,并把xml中的标签封装成BeanDefinition对象
                loadBeanDefinitions(beanFactory);
                synchronized (this.beanFactoryMonitor) {
                    this.beanFactory = beanFactory;
                }
            }
            catch (IOException ex) {
                throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
            }
        }

      1.首先做了判断,如果已经有了BeanFactory,就给他干掉,毕竟一山不容二虎嘛。

      2.其次就是创建BeanFactory,其实里面就是new 了一个DefaultListableBeanFactory对象

      3.设置循环依赖标识(默认就是支持的)

      4.解析xml,封装成BeanDefinition对象(这个对象可是所有bean的胚胎,孕育着所有的bean)

    好了,看到这四步应该都知道哪个重要了吧,我们进去看看xml解析是怎么做的:

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
            // Create a new XmlBeanDefinitionReader for the given BeanFactory.
            //创建xml的解析器,这里是一个委托模式
            XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
            // Configure the bean definition reader with this context's
            // resource loading environment.
            beanDefinitionReader.setEnvironment(this.getEnvironment());
    
            //这里传一个this进去,因为ApplicationContext是实现了ResourceLoader接口的
            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);
        }

    1.首先用了一记委托模式(很多地方都用到了,就不一一介绍了,平时看源码的时候,稍微注意一点应该很容易发现),将xml解析的工作交给了XmlBeanDefinitionReader,一直往下调用loadBeanDefinitions直到:org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions

    public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
            Assert.notNull(resources, "Resource array must not be null");
            int count = 0;
            for (Resource resource : resources) {
                //模板设计模式,调用到子类中的方法
                count += loadBeanDefinitions(resource);
            }
            return count;
        }

    这边使用了模板设计模式,点进去任意选择一个类,可以看到都是实现了AbstractBeanDefinitionReader类的,选择xml解析实现:

                //获取Resource对象中的xml文件流对象
                InputStream inputStream = encodedResource.getResource().getInputStream();
                try {
                    //InputSource是jdk中的sax xml文件解析对象
                    InputSource inputSource = new InputSource(inputStream);
                    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
                    //主要看这个方法 
                    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                }
                finally {
                    inputStream.close();
                }

    获取xml解析对象,继续向下:

                //把inputSource 封装成Document文件对象,这是jdk的API
                Document doc = doLoadDocument(inputSource, resource);
    
                //主要看这个方法,根据解析出来的document对象,拿到里面的标签元素封装成BeanDefinition
                int count = registerBeanDefinitions(doc, resource);
                if (logger.isDebugEnabled()) {
                    logger.debug("Loaded " + count + " bean definitions from " + resource);
                }
                return count;

    再看如何将bean封装成BeanDefinition对象的:

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
            //又来一记委托模式,BeanDefinitionDocumentReader委托这个类进行document的解析
            BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
            int countBefore = getRegistry().getBeanDefinitionCount();
            //主要看这个方法,createReaderContext(resource) XmlReaderContext上下文,封装了XmlBeanDefinitionReader对象
            documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
            return getRegistry().getBeanDefinitionCount() - countBefore;
        }

    又一个委托模式,这是个老板啊,啥都让别人来做,再往下看:registerBeanDefinitions(doc, createReaderContext(resource));一直往下点:

    protected void doRegisterBeanDefinitions(Element root) {
            // Any nested <beans> elements will cause recursion in this method. In
            // order to propagate and preserve <beans> default-* attributes correctly,
            // keep track of the current (parent) delegate, which may be null. Create
            // the new (child) delegate with a reference to the parent for fallback purposes,
            // then ultimately reset this.delegate back to its original (parent) reference.
            // this behavior emulates a stack of delegates without actually necessitating one.
            BeanDefinitionParserDelegate parent = this.delegate;
            this.delegate = createDelegate(getReaderContext(), root, parent);
    
            if (this.delegate.isDefaultNamespace(root)) {
                String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
                if (StringUtils.hasText(profileSpec)) {
                    String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                            profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                    // We cannot use Profiles.of(...) since profile expressions are not supported
                    // in XML config. See SPR-12458 for details.
                    if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                    "] not matching: " + getReaderContext().getResource());
                        }
                        return;
                    }
                }
            }
    
            preProcessXml(root);
    
            //主要看这个方法,标签具体解析过程
            parseBeanDefinitions(root, this.delegate);
            postProcessXml(root);
    
            this.delegate = parent;
        }

    好像很复杂,好像啥也没干。可以看到preProcessXml和postProcessXml方法里面啥也没有,明显是个钩子方法,主要看parseBeanDefinitions(root, this.delegate);

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

    默认标签和自定义标签解析还是比较复杂的,看来一篇是讲不完了,下一篇再好好讲讲这一块。

    总结:

          写到现在,spring连标签解析都没开始,我们已经看到两个设计模式了,用心去看的话,工厂模式也是到处都在,单例模式贯穿了spring。在spring里面用到了很多设计模式和技巧,我们在看源码的同时,稍稍注意一下这些技巧的使用,并想一想,如果是我们要实现这些功能会怎么做,像spring这样的做法是否比你的想法更好呢,或者,如果你是大牛,你是否有更好的方法去实现spring的这一功能,如果有,去github上提交spring,那你在it界就小有名气了,嘿嘿!

        

  • 相关阅读:
    linux command line send email
    js的小随笔
    JavaScript的严格模式
    nodejs学习笔记<六>文件处理
    nodejs学习笔记<五>npm使用
    nodejs学习笔记<四>处理请求参数
    nodejs学习笔记<三>关于路由(url)
    nodejs学习笔记<一>安装及环境搭建
    转:HTTP 301 跳转和302跳转的区别
    前端代码新写法——Zen Coding
  • 原文地址:https://www.cnblogs.com/gmt-hao/p/12973285.html
Copyright © 2011-2022 走看看