美文网首页
XmlBeanFactory创建过程

XmlBeanFactory创建过程

作者: Orange_____ | 来源:发表于2019-08-26 10:25 被阅读0次

    配置文件信息

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="bean" class="club.geek66.spring.beans.beanfactory.xml.TestBean.Bean"/>
        
    </beans>
    

    TestCase代码如下

    @Test
    public void test() {
        XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("/spring.xml"));
    }
    

    进入XmlBeanFactory的构造方法中, 该构造方法代码如下

    // 需要一个Resource类型的对象
    // 它用来表示xml配置文件的位置
    // 它可以是classpath类型, url又或者是fileSystem
    // 刚才创建的是一个`ClassPathResource`对象, 表示的是我们的配置文件是在类路径下的
    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }
    

    在该构造方法中它调用了自身的另一个构造方法, 该构造方法代码如下

    // resource: 配置文件
    // parentBeanFactory: 父BeanFactory, 不需要关心, 因为传入的值为`null`
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        // 加载Bean的定义, 需要Resource
        this.reader.loadBeanDefinitions(resource); // 加载Bean的定义
    }
    

    主要代码

    this.reader.loadBeanDefinitions(resource);
    
    // reader是它自身的一个字段, 这个字段的声明代码如下
    // private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
    

    通过名字应该可以看出来这个对象实际上就是用来加载BeanDefinition
    紧接着进入该方法

    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource)); // new EncodedResource(resource) 将资源进行编码
    }
    

    它调用了重载方法

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
            logger.info("Loading XML bean definitions from " + encodedResource);
        }
    
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
    
            // 从Resource里获取流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                // doxxx 表示真正干活的, 前面的逻辑是做了一些简单的设置,
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            } finally {
                inputStream.close();
            }
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        } finally {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }
    

    这个方法里前面的代码是做了一些简单的设置, 比如创建了inputSource对象, 这个对象是sax包里的(解析xml文档的包), 目前只需要关心这一行代码

    return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); // 真正干活的逻辑
    // 在spring中, 一般doxxx开头的都表示真正的干活逻辑, 例如springmvc中DispatcherServlet的doDispatch方法
    

    里面具体代码如下

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
    
            // 1.将resource转为Document对象
            Document doc = doLoadDocument(inputSource, resource);
    
            // 2.注册Bean定义
            return registerBeanDefinitions(doc, resource);
        } catch (BeanDefinitionStoreException ex) {
            throw ex;
        } catch (SAXParseException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        } catch (SAXException ex) {
            throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                    "XML document from " + resource + " is invalid", ex);
        } catch (ParserConfigurationException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Parser configuration exception parsing XML from " + resource, ex);
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "IOException parsing XML document from " + resource, ex);
        } catch (Throwable ex) {
            throw new BeanDefinitionStoreException(resource.getDescription(),
                    "Unexpected exception parsing XML document from " + resource, ex);
        }
    }
    

    上面方法里主要代码

    // 1.将resource转为Document对象
    Document doc = doLoadDocument(inputSource, resource);
    
    // 2.注册Bean定义
    return registerBeanDefinitions(doc, resource);
    

    分为了两部曲

    1. 加载XmlDocument
    2. 注册BeanDefinition, 需要doc这个对象, 因为这个代表了我们的配置文件

    读取Xml的话这里暂时不研究, 重点关注第二步, 注册Bean的定义, 代码如下

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    
        // BeanDefinition读取, 读取Document中的Definition
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    
        // 已经存在的BeanDefinition数量
        int countBefore = getRegistry().getBeanDefinitionCount();
    
        // 注册BeanBeanDefinition
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    

    这里面干了三件事

    1. createBeanDefinitionDocumentReader(), 方法比较简单, 创建了一个创建了documentReader对象为DefaultBeanDefinitionDocumentReader类型
    2. 注册BeanDefinition
    3. 返回注册BeanDefinition的数量,

    前面比较简单, 重点在注册BeanDefinition的这一行

    // 1. Document类型 document对象, 这个对象代表的是刚刚的xml配置文件, 里面包含配置文件的内容
    // 2. XmlReaderContext类型 reader的context, context是上下文的意思, 一般存放着对象依赖的一些对象或者是数据, 可以理解成reader依赖的一些对象包含在这个对象里
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    

    第一个doc对象是之前创建好的,
    第二个参数调用了createReaderContext方法来创建对象, 分析这个方法, 看样子应该是创建readerContext, 这个对象名字听起来像是创造了一个reader的上下文对象, 进入创建readerContext这个方法里

    public XmlReaderContext createReaderContext(Resource resource) {
        return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                this.sourceExtractor, this, getNamespaceHandlerResolver());
    }
    

    构造XmlReaderContext所需要的对象

    类型 功能
    Resource 配置文件
    ProblemReporter 在解析xml中出现问题的时候会调用该类的方法, 例如fatal(致命错误), error(错误), warning(警告)
    ReaderEventListener 事件监听器, 一般当读取到文档里特定的节点时会触发
    SourceExtractor 这个没研究暂时跳过
    XmlBeanDefinitionReader reader对象
    NamespaceHandlerResolver 解析自定义的namespace, 比如spring里提供的<context:component-scan />标签就是由特定的NamespaceHandler来处理

    下面返回这个方法, 再回到刚刚的方法, 开始查看具体注册BeanDefinition的逻辑

    documentReader.registerBeanDefinitions(doc, createReaderContext(resource))
    

    documentReader实际上是由DefaultBeanDefinitionDocumentReader这个类负责处理的, 方法代码

    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
    
        // 获取document的根元素
        Element root = doc.getDocumentElement();
            
        // 注册BeanDefinitions
        doRegisterBeanDefinitions(root);
    }
    

    前面是拿到根节点, 它就是平常定义的<beans></beans>
    进入doRegisterBeanDefinitions(root)

    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);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("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;
    }
    

    这个方法里有一段注释, 大概意思是, 如果根beans标签下面又解析到beans标签的时候会发生递归调用, 为什么要递归调用,原因是因为可以正确的继承它上层beans标签上的default-*属性, 每次解析beans标签都会创建一个BeanDefinitionParserDelegate对象, 然后委托他来进行解析我们定义的标签

    比如我定义了嵌套的beans标签, 其中外层的beans标签声明了default-lazt-init, 将它设为true, 表示默认情况下该标签下的bean声明的bean默认延迟加载, 有些情况下, 可能我会嵌套很多beans标签在这个标签里, 这个时候, 里层beans标签它将继承外层声明属性, 前提是它自己不去指定值的情况下

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd" default-lazy-init="true">
        <beans>
            <bean id="bean" class="club.geek66.spring.beans.beanfactory.xml.TestBean.Bean"/>
        </beans>
    </beans>
    
    属性 功能
    default-lazy-init 默认是否延迟加载(ture或者false或者default, default等同false)
    default-autowire 默认的装配方式(byName, byType, constructor, no, default default等同no)
    ... ...

    看一下它是怎么工作的, 其实代码也很简单, 分析刚才的方法, 首先它先获取了父委托对象, 然后创建自己的委托
    this.delegate = createDelegate(getReaderContext(), root, parent);这行代码是负责创建委托对象的, 进入这个方法一探究竟

    protected BeanDefinitionParserDelegate createDelegate(
            XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {
    
        BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
    
        // 初始化默认值
        delegate.initDefaults(root, parentDelegate);
        return delegate;
    }
    

    创建对象, 第二步初始化默认值, 比较简单, 进入delegate.initDefaults(root, parentDelegate);

    public void initDefaults(Element root, @Nullable BeanDefinitionParserDelegate parent) {
    
        // 填充默认值
        populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);
    
        // 调用钩子方法
        this.readerContext.fireDefaultsRegistered(this.defaults);
    }
    

    1.填充默认值传三个参数this.defaults, parent.defaults存在则传入, 否则为null, 标签元素
    进入这个方法

    protected void populateDefaults(DocumentDefaultsDefinition defaults, @Nullable DocumentDefaultsDefinition parentDefaults, Element root) {
    
    
        // 1.先给default-lazy-init, default-merge, default-autowire设置默认值
        // 首先从标签里拿属性, 然后判断它是否为默认值default, 
        // 如果是默认值, 则给该属性赋值, 如果父亲存在用父亲的否则用默认的, 
        // 这个默认值就是具体的默认值, 例如false, no
        String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);
        if (isDefaultValue(lazyInit)) {
            // Potentially inherited from outer <beans> sections, otherwise falling back to false.
            lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
        }
        defaults.setLazyInit(lazyInit);
    
        String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE);
        if (isDefaultValue(merge)) {
            // Potentially inherited from outer <beans> sections, otherwise falling back to false.
            merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE);
        }
        defaults.setMerge(merge);
    
        String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE);
        if (isDefaultValue(autowire)) {
            // Potentially inherited from outer <beans> sections, otherwise falling back to 'no'.
            autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE);
        }
        defaults.setAutowire(autowire);
    
    
        // 下面的属性如果存在才设置
        if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {
            defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));
        }
        else if (parentDefaults != null) {
            defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates());
        }
    
        if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {
            defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));
        }
        else if (parentDefaults != null) {
            defaults.setInitMethod(parentDefaults.getInitMethod());
        }
    
        if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {
            defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));
        }
        else if (parentDefaults != null) {
            defaults.setDestroyMethod(parentDefaults.getDestroyMethod());
        }
    
        defaults.setSource(this.readerContext.extractSource(root));
    }
    

    大概就是设置了一些默认值, 到这个时候delegate就创建好了,
    接下来回到刚刚的方法, 下一步解析标签里面的标签了

    preProcessXml(root);
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);
    

    在具体解析标签的方法前后有两个钩子方法, 如果有特殊需求可以继承这个类然后写自己的逻辑
    进入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)) {
    
                        // 解析默认的元素, 也就是spring中提供的, 例如<bean>
                        parseDefaultElement(ele, delegate);
                    }
                    else {
    
                        // // 解析自定义的标签
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
    
            // 解析自定义的标签
            delegate.parseCustomElement(root);
        }
    }
    

    大概意思是如果根标签是默认的namespace, "http://www.springframework.org/schema/beans", 它会去解析它自己的, 否则就去解析自定义的标签了,
    我们分两部分析, 先看它是如何解析它自己提供的标签然后再看解析我们自定义的标签

    1. 解析spring自己定义的标签
      先拿到该标签下的标签, 然后解析
      进入parseDefaultElement(ele, delegate);
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    
        // import标签
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
    
        // alias标签
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
    
        // bean标签
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
    
        // 嵌套的beans标签, 会调用刚刚递归的方法
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }
    

    大概就是解析4类标签
    <import> <alias> <bean> <beans>
    挑一个重要的看

    未完待续...

    总结
    参与到的类
    XmlBeanFactory

    XmlBeanDefinitionReader (加载resouce为Document然后调用BeanDefinitionDocumentReader)

    BeanDefinitionDocumentReader (调用BeanDefinitionParserDelegate去解析节点)

    BeanDefinitionParserDelegate (具体去解析标签)

    XmlReaderContext (reader的上下文对象, 里面存放着一系列reader所依赖的对象)

    相关文章

      网友评论

          本文标题:XmlBeanFactory创建过程

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