zoukankan      html  css  js  c++  java
  • 《Spring源码深度解析》二

    第二章:容器的基本实现

    2.1 基本用法

    首先定义一个Bean:(假设在bean包下)

    《Spring源码深度解析》二

    然后定义配置文件:

    《Spring源码深度解析》二

    测试类:

    《Spring源码深度解析》二

    当然,这并非是企业级用法,此处只是用来分析学习其实现

    2.2 功能分析

    上述三图代码功能如下:

    1.读取配置文件beanFactocyTest.xml

    2.根据beanFactocyTest.xml中的配置找到对应的类的配置,并实例化

    3.调用实例化后的对象

    可以猜测,总共需要三个类:ConfigReader用来读取配置文件,ReflectionUtil用来反射实例化配置中的bean,App用于完成整个逻辑的串联

    架构图:

    《Spring源码深度解析》二

    下面就来看看Sping的实现

    2.3 org.springframework.beans初步介绍

    2.3.1 工程目录

    《Spring源码深度解析》二

    2.3.2 两个核心类

    2.3.2.1 DefaultListableBeanFactory

    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
            implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable

    整个bean加载的核心部分,是Spring注册及加载bean的默认实现

    XmlBeanFactory继承此类,使用了自定义的XML读取器XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取,主要使用reader属性对资源文件进行读取和注册,而注册及获取bean都是使用DefaultListableBeanFactory的方法

    类结构图:idea中F4可打开

    《Spring源码深度解析》二

    容器加载相关类图及其作用:

    《Spring源码深度解析》二

    2.3.2.2 XmlBeanDefinitionReader

    即2.3.2.1中解释的XmlBeanFactory使用的自定义XML读取器,实现对资源文件的读取、解析及注册,即Resource相关类完成了对配置文件的封装后,它会完成配置文件的读取工作

    配置文件读取相关类图及其作用:

    《Spring源码深度解析》二

    XmlBeanDefinitionReader处理流程:
    1.通过继承的AbstractBeanDefinitionReader的方法,使用ResourLoader将资源文件路径转换为对应的Resouce文件
    2.通过DocumentLoader将Resouce文件转换为Document文件
    3.通过DefaultBeanDefinitionDocumentReader对Document进行解析,并使用BeanDefinitionParserDelegate进行解析

    2.4 2.1中代码的功能实现

    2.4.1 BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));

    处理时序图:

    《Spring源码深度解析》二

    流程:首先创建和封装Resource对象,传给XmlBeanFactory后,调用loadBeanDefinitions,最终返回BeanFactory对象。

    那么,如何封装Resource资源呢?

    2.4.1.1 封装Resource资源

    ClassPathResource:封装配置文件,使用Resource接口封装底层资源,如File、URL、Classpath等
    底层资源注:不同来源的资源为URL,通过注册不同的handler(如URLStreamHandler)来处理不同来源的读取逻辑;handler使用前缀+协议(Protocol)属性来识别不同来源,如"file:""http:""jar:"等

    Resource相关接口:

    《Spring源码深度解析》二

    不同来源的资源对应的Resource实现:

    《Spring源码深度解析》二

    getInputStream方法:

    从实现类的类名可以推出,实现类组合了其类名的类,如下两个例子:
    ClassPathResource:通过class或classLoader提供的底层方法进行调用,组合了path

    《Spring源码深度解析》二

    FileSystemResource:使用FileInputStream对文件进行实例化,组合了file

    《Spring源码深度解析》二

    技巧:日常开发中,可以直接使用Spring提供的类来加载资源,如下图:

    《Spring源码深度解析》二

    2.4.1.2 流程的其他步骤实现

    XmlBeanFactory初始化,使用Resourse示例作为构造方法参数:

    《Spring源码深度解析》二

    首先会调用其间接父类AbstractAutowireCapableBeanFactory的构造方法:

    《Spring源码深度解析》二

    其ignoreDependencyInterface方法主要功能是忽略给定接口的自动装配功能,为什么要有这个方法呢?

    《Spring源码深度解析》二

    接着回到XmlBeanFactory构造方法的loadBeanDefinitions(resource),这个方法是整个资源加载的切入点

    时序图:

    《Spring源码深度解析》二

    其处理过程如下:(其中2.通过SAX读取XML文件来准备InputSource)

    《Spring源码深度解析》二

    EncodedResource封装了有编码的InputStreamReader
    构造好EncodedResource后,就进入了真正的数据准备阶段loadBeanDefinitions,即"处理时序图"所示的第三阶段
    这个方法的逻辑核心部分为doLoadBeanDefinitions,做了下列三件事情:

    《Spring源码深度解析》二

    这三件事情,我们来逐步分析

    1.获取对XML文件的验证模式:getValidationModeForResource (Resource resource)
    a 常用的验证模式:DTD、XSD
    可以通过比较XML文档和DTD 文件来看文档是否符合规范,元素和标签使用是否正确
    也可以用一个指定的XML Schema(本身也是XML)来验证某个XML 文档, 以检查该XML文档是否符合其要求
    b 验证模式的读取,可以通过setValidationMode进行设定验证模式
    流程代码:

    《Spring源码深度解析》二

    detectValidationMode方法使用委派模式,委托给了XmlValiadationModeDetector(通过组合此类)的validationModeDetector方法来实现自动检测验证模式:
    判断是否包含DOCTYPE ,如果包含就是DTD ,否则就是XSD

    2.加载XML文件,并得到对应的Document:loadDocument

    同样地,XmlBeanFactoryReader将文档读取任务委托给了DocumentLoader接口,真正调用的实现类为DefaultDocumentLoader,通过SAX来解析XML文档(相同的套路,首先创建DocumentBuilderFactory ,再通
    过DocumentBuilderFactory创建DocumentBuilder, 进而解析inputSource来返回Document 对象。)
    loadDocument方法中涉及一个参数EntityResolver,下面解释一下

    2.1 EntityResolver

    可以提供一个如何寻找DTD声明的方法,即由程序来实现寻找DTD声明的过程,比如我们将DTD文件放到项目中某处,在实现时直接将此文档读取并返回给SAX 即可。这样就避免了通过网络来寻找相应的声明,加快速度。
    2.1.1 通过getEntityResolver方法获取

    《Spring源码深度解析》二

    2.1.2 如何将URL转换为工程对应的地址文件呢?
    对不同的验证模式,Spring 使用了不同的解析器解析
    比如加载DTD类型的BeansDtdResolver的resolveEntity是直接截取systemld 最后的xx.dtd
    然后去当前路径下寻找,而加载XSD类型的PluggableSchemaResolver 类的resolveEntity 是默
    认到META-INF/Spring.schemas 文件巾找到systemid 所对应的XSD 文件并加载。

    3.根据返回的Document注册Bean信息

    拥有Documemt对象时,调用下面方法

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //实例化
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        //记录统计前BeanDefinition的加载个数
        int countBefore = getRegistry().getBeanDefinitionCount();
        //加载注册bean
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        //记录本次加载的BeanDefinition个数
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        doRegisterBeanDefinitions(doc.getDocumentElement());
    }

    DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions(doc.getDocumentElement());真正地开始解析:

    protected void doRegisterBeanDefinitions(Element root) {
        //专门处理解析
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            //处理profile属性
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);

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

    涉及模板方法模式,DefaultBeanDefinitionDocumentReader的子类如果需要在Bean解析前后做一些处理的话,那么只需要重写preProcessXml(root)和postProcessXml(root))

    而parseBeanDefinitions则对不同类Bean分别做不同的处理

    /**
     * 处理两大类Bean声明
     * 一个是默认的,如:<bean id=”test” class=”test.TestBean” />
     * 另一类就是自定义的,如:<tx :annotation-driven/>
     * @param root
     * @param 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);
        }
    }

    总结

    DefaultListableBeanFactory和XmlBeanDefinitionReader是Spring容器基本实现的两个核心类

    DefaultListableBeanFactory是整个bean加载的核心部分,是Spring注册及加载bean的默认实现

    XmlBeanDefinitionReader则是XmlBeanFactory使用的自定义XML读取器,实现对资源文件的读取、解析及注册

    创建BeanFactory的流程为:首先创建和封装Resource对象,传给XmlBeanFactory后,调用loadBeanDefinitions,最终返回BeanFactory对象。

  • 相关阅读:
    swift5.x 错误处理
    iOS--iPhoneX设备判断
    swift5.x 多线程的应用场景
    Swift5.x 多线程编程
    Swift5.x 范型
    swift5.x 类初始化、反初始化, 指定初始化器,便利初始化器
    swift5.x 扩展(extension)
    swift5.x 多态,类型检查,类型转换
    Effective java笔记(十),序列化
    Effective java笔记(九),并发
  • 原文地址:https://www.cnblogs.com/mxb0611/p/11926856.html
Copyright © 2011-2022 走看看