[Spring]BeanDefinitionReader-将xml解析到BeanDefinition的执行者

BeanDefinitionReader

  • 读取BeanDefinition
  • 借助BeanDefinitionRegistry将BeanDefinition注册到容器中

接口清单

public interface BeanDefinitionReader {
    // 获取BeanDefinitionRegistry对象,将bd注册进容器中
    BeanDefinitionRegistry getRegistry();
 
    // 获取ResourceLoader
    ResourceLoader getResourceLoader();

    // 获取类加载器
    ClassLoader getBeanClassLoader();

    // Bean的名字生成器,为没有指定name的bean生成一个名字
    BeanNameGenerator getBeanNameGenerator();

    // 加载单个resource
    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

    // 加载多个resource
    int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

    // 加载单个配置文件路径
    // 该位置也可以是位置模式,前提是此bean定义读取器的ResourceLoader是ResourcePatternResolver。
    int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
    
    // 加载多个配置文件路径
    int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}

UML

UML
  • AbstractBeanDefinitionReader:BeanDefinitionReader接口的抽象实现类,其中,加载多个资源的方法为org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)。同时,他还实现了EnvironmentCapable,提供了获取/设置环境的能力。
  • XmlBeanDefinitionReader: 主要用于读取 XML 文件,将元素加载到 BeanDefinition.
  • PropertiesBeanDefinitionReader:提供Map / Properties和ResourceBundle的bean定义注册方法。通常应用于DefaultListableBeanFactory

导读

  • 概览


    概览
  • 细览


    细览

核心方法

loadBeanDefinitions

获取ResourceLoader的实例,然后判断该实例是属于ResourcePatternResolver还是默认的ResourceLoader实例。ResourcePatternResolver代表加载多个资源,否则加载单个资源。在确认加载多个还是单个后,都会调用loadBeanDefinitions(Resource resource)进行资源加载。

    public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
        // 获取资源加载器,主要的功能是根据路径和类加载器获取Resource资源
        ResourceLoader resourceLoader = getResourceLoader();
        // 判断资源加载器是否为空
        if (resourceLoader == null) {
            throw new BeanDefinitionStoreException(
                    "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
        }
        // ResourcePatternResolver用于加载多个文件或者能够加载 ant风格路径的文件资源
        if (resourceLoader instanceof ResourcePatternResolver) {
            // Resource pattern matching available.
            try {
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                int count = loadBeanDefinitions(resources);
                if (actualResources != null) {
                    Collections.addAll(actualResources, resources);
                }
                if (logger.isTraceEnabled()) {
                    logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
                }
                return count;
            }
            catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        }
        else {
            // Can only load single resources by absolute URL.
            // 加载单个资源文件,通过绝对路径,直接使用ResourceLoader加载
            Resource resource = resourceLoader.getResource(location);
            int count = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
            }
            return count;
        }
    }

XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)

在AbstractBeanDefinitionReader并不会实现这个方法(抽象类 实现接口时,可以不实现部分接口方法),而是交由子类去做最终的实现。这里介绍的是经典的xml配置通过XmlBeanDefinitionReader加载到BeanDefinition的过程.

  • 1.启动xml容器
start

在启动前,通过IDEA将断点打在org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions上.

  • 2. 查看堆栈信息
    通过调用堆栈,我们可以看到,加载资源的步骤为(从下往上看):
debug
  • 容器的refresh()方法.
  • AbstractXmlApplicationContextloadBeanDefinitions.
    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);
    }

这里说明一下,我们通常传入到容器的都是加载资源的位置,即configLocations,Spring通过解析这个字符串得到ResourLoader,再通过ResourceLoader和BeanDefinitionReader得到BeanDefinition.

configLocations
  • AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String...)

这里稍微简单,就是通过传入的多个location,循环加载BeanDefinition.

    public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
        Assert.notNull(locations, "Location array must not be null");
        int count = 0;
        for (String location : locations) {
            count += loadBeanDefinitions(location);
        }
        return count;
    }
  • AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String, java.util.Set<org.springframework.core.io.Resource>)

看到这里就是上面的抽象类通用方法了,由于当前容器是FileSystemXmlApplicationContext,它扩展的上层接口中有ApplicationContext,通过我的前几篇文章你可以得知,ApplicationContext接口是扩展了ResourcePatternResolver接口的,由此在AbstractBeanDefinitionReader中就会将当前的ResourceLoader视作为ResourcePatternResolver的实例。

  • AbstractBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource...)

上面说明了,如果是ResourcePatternResolver,会进行多个资源加载。在这里就会进入到XmlBeanDefinitionReader#loadBeanDefinitions(Resource resource)中.

ResourceLoader
  • XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)

加载Resource,并指定编码

    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }
  • XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)

Spring使用了ThreadLocal来存储加载的resource,进而使用多线程加载资源,这使得多个线程之间加载的resource互不冲突.

    // 用ThreadLocal来存储正在加载的资源
    private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
            new NamedThreadLocal<>("XML bean definition resources currently being loaded");
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isTraceEnabled()) {
            logger.trace("Loading XML bean definitions from " + encodedResource);
        }
        // 多线程加载Resource,每个本地线程存储自己正在加载的resource,其他线程无法访问和修改
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        // 如果本地线程变量中不存在正在加载的资源,那么将其添加进去
        if (currentResources == null) {
            currentResources = new HashSet<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        // 如果往currentResources添加encodedResource失败,表明已经存在资源,只不过未加载完成,防止同一个线程多次加载同一个文件
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            // 获取文件的输入流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                // 封装成InputSource,其中指定了输入流和编码格式
                InputSource inputSource = new InputSource(inputStream);
                // 如果存在编码,那么将其添加进InputSource中
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                // 继续加载 bd
                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();
            }
        }
    }
  • org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions

    1. Document:通过debug,可以从Document的fNodeValue看到内容。
fNodeValue
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

        try {
            // 创建Document对象,XML的文档对象,就是DOM树
            // 使用这个Document可以获取XML文件中的节点并且创建节点
            // 使用 SAX的方式解析XML
            Document doc = doLoadDocument(inputSource, resource);
            // 解析dom树,即解析出一个个属性,然后将其保存到bd中
            // 向容器注册bd
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        }
        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);
        }
    }
  • org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions

获取BeanDefinitionDocumentReader,读取配置到BeanDefinition中,然后向容器注册BeanDefinition,可以看到有前后对比注册的BeanDefinition的逻辑.在这里,getRegistry()指向的是DefaultListableBeanFactory,getBeanDefinitionCount()指向的是DefaultListableBeanFactory容器的beanDefinitionMap.size()

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 创建BeanDefinitionDocumentReader,这个实际从XML的DOM树中读取bd
        // 实际进行工作的是 DefaultBeanDefinitionDocumentReader
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        // 未注册前的bd数量
        int countBefore = getRegistry().getBeanDefinitionCount();
        // 注册bd
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        // 注册后的bd数量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
  • org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions

DefaultBeanDefinitionDocumentReader开始解析xml文件->doRegisterBeanDefinitions

    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        // 根据符合spring DTD 的 xml 解析 BeanDefinition
        doRegisterBeanDefinitions(doc.getDocumentElement());
    }
  • org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions

在这里,DefaultBeanDefinitionDocumentReader将解析xml的任务委托给了BeanDefinitionParserDelegate进行。这里关注一个真正解析xml的方法->parseBeanDefinitions(root, this.delegate);

    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.
        // bd解析委托类
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
        // 判断root节点是否是默认的命名空间
        // nameSpaceUrl == "http://www.springframework.org/schema/beans"
        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);
                // We cannot use Profiles.of(...) since profile expressions are not supported
                // in XML config. See SPR-12458 for details.
                // 判断这个切面是否是active的环境,如果不是直接返回,表示这个配置文件不是当前环境的配置文件
                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;
                }

            }
        }
        // 在解析xml之前的准备工作
        preProcessXml(root);
        // 解析xml
        parseBeanDefinitions(root, this.delegate);
        // 解析完xml后的钩子方法
        postProcessXml(root);

        this.delegate = parent;
    }
  • org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

从根结点开始遍历子结点,对子结点进行解析。parseDefaultElement(ele,delegate)则是Spring解析符合其命名规则的逻辑了.

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;
                // 如果符合Spring的命名规则,对该标签进行解析
                if (delegate.isDefaultNamespace(ele)) {
                    parseDefaultElement(ele, delegate);
                }
                else {
                    // 解析用户自定义的规则
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}
  • org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseDefaultElement

对于每个子结点,也有可能出现<beans/>标签,Spring会进行递归调用。这里关注bean的解析-> processBeanDefinition(ele, delegate);

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        // 如果该节点名称等于"import",即<import resource="classpath:xxx.xml"/>,对xml文件进行导入解析
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        // 如果该节点名称为"alias",即<Alias>,对bean别名进行解析
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        // 如果该节点名称为"bean",即最常见的<Bean>,进行Bean解析
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        // beans
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // 递归调用doRegisterBeanDefinitions,再次重复解析xml的过程
            doRegisterBeanDefinitions(ele);
        }
    }
  • org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition

parseBeanDefinitionElement(ele)就是开始解析bean标签的内容了,拨开层层迷雾,终于看到了解析bean标签的地方。

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        // BeanDefinitionHolder->对BeanDefinition的封装
        // 对Document对象中<bean>元素的解析委托给BeanDefinitionParserDelegate实现
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 向Spring IOC容器注册解析得到的BeanDefinition,这是BeanDefinition向容器注册的入口
                // Register the final decorated instance.
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // 向容器发送注册事完成件通知
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }
  • org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)

使用GenericBeanDefinition进行Bean属性的装载->AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        // 获取id
        String id = ele.getAttribute(ID_ATTRIBUTE);
        // 获取name
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        List<String> aliases = new ArrayList<>();
        // 将bean元素的name放入别名数组中,spring支持两种方式定义别名,一种是通过<Alias>,另一种是<bean name=""/>
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
            if (logger.isTraceEnabled()) {
                logger.trace("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }

        if (containingBean == null) {
            // 检测<bean>中的name和id是否唯一
            checkNameUniqueness(beanName, aliases, ele);
        }
        // 对bean标签的其他属性进行解析,5.1X版本中,Spring使用了GenericBeanDefinition来解析BeanDefinition
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        // 赋予名称
        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        // Register an alias for the plain bean class name, if still possible,
                        // if the generator returned the class name plus a suffix.
                        // This is expected for Spring 1.2/2.0 backwards compatibility.
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&
                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }
                    if (logger.isTraceEnabled()) {
                        logger.trace("Neither XML 'id' nor 'name' specified - " +
                                "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
    }
  • org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionElement(org.w3c.dom.Element, java.lang.String, org.springframework.beans.factory.config.BeanDefinition)
    public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, @Nullable BeanDefinition containingBean) {

        this.parseState.push(new BeanEntry(beanName));

        String className = null;
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }
        String parent = null;
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }

        try {
            // 将class与parentName作为参数创建BeanDefinition
            AbstractBeanDefinition bd = createBeanDefinition(className, parent);
            // 解析bean中的属性值,通过set方法将属性进行注入
            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

            parseMetaElements(ele, bd);
            // look up method
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            // replace method
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
            // 解析构造函数
            parseConstructorArgElements(ele, bd);
            parsePropertyElements(ele, bd);
            parseQualifierElements(ele, bd);

            bd.setResource(this.readerContext.getResource());
            bd.setSource(extractSource(ele));

            return bd;
        }
        catch (ClassNotFoundException ex) {
            error("Bean class [" + className + "] not found", ele, ex);
        }
        catch (NoClassDefFoundError err) {
            error("Class that bean class [" + className + "] depends on not found", ele, err);
        }
        catch (Throwable ex) {
            error("Unexpected failure during bean definition parsing", ele, ex);
        }
        finally {
            this.parseState.pop();
        }

        return null;
    }
  • org.springframework.beans.factory.support.BeanDefinitionReaderUtils#createBeanDefinition

联系之前学习的GenericBeanDefinition,这里可以考证,Spring的确是用GenericBeanDefinition对Bean进行定义的。联系上面的代码块,可以看到Spring创建了GenericBeanDefinition并解析其中的标签内容,将这些属性注入到GenericBeanDefinition中.

    public static AbstractBeanDefinition createBeanDefinition(
            @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setParentName(parentName);
        if (className != null) {
            if (classLoader != null) {
                bd.setBeanClass(ClassUtils.forName(className, classLoader));
            }
            else {
                bd.setBeanClassName(className);
            }
        }
        return bd;
    }
  • 至此,我们可以看到Spring加载xml并装配到BeanDefinition的详细过程了,完成这一部之后,Spring就会通过BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());将每个BeanDefinition注册到容器中

  • 发送事件通知容器,完成了一个bean的注册事件

  • refresh中的obtainFreshBeanFactory()方法执行完毕,容器继续其他的行为

总结

  • BeanDefinitionReader是解析BeanDefinition的执行者
  • BeadDefinitionReader使用ResourceLoader进行资源定位
  • GenericBeanDefinition为真正装载BeanDefinition属性的类
  • 完成BeanDefinition的装载后,BeanDefinitionReader会对该BeanDefinition进行注册
  • 完成注册后,Spring会发送注册完成通知事件
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容