Spring源码(二)-XML文件的读取-BeanDefinitionReader

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 我们已经知道Spring会将 ,@Bean,@Component 等方式所定义的 Bean,最终都会解析为 BeanDefinition 对象。
日积月累,水滴石穿 😄

我们已经知道Spring会将 bean标签@Bean@Component 等方式所定义的 Bean,最终都会被解析为 BeanDefinition 对象。那 Spring 是怎么读取 xml 配置文件或者说是怎么解析被 @Bean 等注解标注的方法或类并最终成了BeanDefinition呢?(注解方式之后再说)

BeanDefinitionReader

BeanDefinitionReader 的作用是读取 Spring配置文件中的内容,将之解析为BeanDefinition并注册到 BeanDefinitionRegistry 工厂中。


接下来就一起来看看 BeanDefinitionReader 的定义

public interface BeanDefinitionReader { 

    //返回用于注册bean定义的bean工厂
    BeanDefinitionRegistry getRegistry();

    //资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource
    @Nullable
    ResourceLoader getResourceLoader();

    //返回类加载器。
    @Nullable
    ClassLoader getBeanClassLoader();

    //BeanName生成器
    //为没有明确指定bean名称的Bean生成一个名称
    BeanNameGenerator getBeanNameGenerator();

    //从指定的资源加载bean定义,返回找到的bean定义的数量
    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

    //指定多个资源加载bean定义,返回找到的bean定义的数量
    int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

    //从指定的资源位置加载bean定义
    //该位置也可以是位置模式,前提是此bean定义读取器的ResourceLoader是ResourcePatternResolver。
    int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;

    //加载多个配置文件路径
    int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}

BeanDefinitionReader实现类

BeanDefinitionReader是一个接口,它有多个实现类,那我们看看它一共有多少个子类吧!
image.png
可以看到 BeanDefinitionReader 下有一个抽象子类 AbstractBeanDefinitionReaderAbstractBeanDefinitionReader下有三个子类。

再来看一张继承关系图:
image.png

  • AbstractBeanDefinitionReader:为 BeanDefinitionReader 接口的抽象实现类,实现了 EnvironmentCapable,提供了获取/设置环境的方法
  • XmlBeanDefinitionReader:读取 XML 文件定义的 BeanDefinition
  • PropertiesBeanDefinitionReader:可以从属性文件,Resource,Property 对象等读取 BeanDefinition
  • GroovyBeanDefinitionReader:可以读取 Groovy 语言定义的 Bean

AbstractBeanDefinitionReader

该类实现了 BeanDefinitionReaderEnvironmentCapable 接口的抽象类,提供属性:注册 bean 定义的bean 工厂、资源加载器、加载 bean 类的类加载器、环境、BeanName 生成器。具体定义如下:

//注册bean定义的bean工厂
    private final BeanDefinitionRegistry registry;
//资源加载器
    @Nullable
    private ResourceLoader resourceLoader;
//加载 bean 类的类加载器
    @Nullable
    private ClassLoader beanClassLoader;
//环境
    private Environment environment;
//BeanName生成器
    private BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE;

该类最核心的方法是 loadBeanDefinitions()方法,所以接下来我们主要就是分析该方法:

public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
    Assert.notNull(locations, "Location array must not be null");
    int count = 0;
    String[] var3 = locations;
    int var4 = locations.length;

    for(int var5 = 0; var5 < var4; ++var5) {
        String location = var3[var5];
        count += this.loadBeanDefinitions(location);
    }

    return count;
}

当传入的参数为资源位置数组时,进入上述方法,遍历调用 loadBeanDefinitions(location)方法。其定义如下:

public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
    return this.loadBeanDefinitions(location, (Set)null);
}

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    //获取资源加载器
    ResourceLoader resourceLoader = this.getResourceLoader();
  // 判断资源加载器是否为空,为空抛出异常
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException("Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
    } else {
        int count;
        if (resourceLoader instanceof ResourcePatternResolver) {
            try {
                //根据资源路径调用ResourcePatternResolver的getResources方法,此方法可以加载多个资源
                Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
                //根据资源来加载bean定义,调用本类loadBeanDefinitions(Resource... resources)方法
                count = loadBeanDefinitions(resource);
                //方法参数,由上层传入
                if (actualResources != null) {
                    Collections.addAll(actualResources, resources);
                }

                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
                }

                return count;
            } catch (IOException var6) {
                throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var6);
            }
        } else {
            //此方法只能加载一个资源
            Resource resource = resourceLoader.getResource(location);
           //调用父类的loadBeanDefinitions(resources)方法,然后走不同的子类实现
            count = loadBeanDefinitions(resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }

            if (this.logger.isTraceEnabled()) {
                this.logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
            }

            return count;
        }
    }
}

@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
    Assert.notNull(resources, "Resource array must not be null");
    int count = 0;
    for (Resource resource : resources) {
//循环调用父类loadBeanDefinitions(resources)方法,然后走不同的子类实现
        count += loadBeanDefinitions(resource);
    }
    return count;
}

根据资源加载器的不同,来处理资源路径,从而返回多个或一个资源,然后再将资源作为参数传递给 loadBeanDefinitions(Resource... resources)方法,该方法用于处理多个资源,归根结底,最后还是调用 BeanDefinitionReader#loadBeanDefinitions(resource)方法,该方法的具体实现就交给了子类进行处理

XmlBeanDefinitionReader

该类作为 AbstractBeanDefinitionReader 的扩展类,继承了 AbstractBeanDefinitionReader 所有的方法,同时也扩展了很多新的方法,主要用于读取 XML 文件中定义的 bean。具体使用如下:

public static void main(String[] args) {
       //设置加载环境
        //System.setProperty("spring.profiles.active", "dev");

        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        // 指定具体子类
        AbstractBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        int i = reader.loadBeanDefinitions("spring-config.xml");
        System.out.println("本次加载的Bean数量:" + i);
        // 根据BeanName获取Bean
        User user = (User)factory.getBean("user");
        System.out.println("user" + user);
        User.Vip vip = user.getVip();
        System.out.println("vip:" + vip.getVipLevel());

        //根据别名获取Bean
        User user2 = (User)factory.getBean("user2");
        System.out.println("user2:" + user2);
        User user4 = (User)factory.getBean("user4");
        System.out.println("user4:" + user4);
        //根据bean名称获取别名数组
        String[] users = factory.getAliases("user");
        for (String s : users) {
            System.out.println(s);
        }
    }
结果:====
本次加载的Bean数量:1
userUser{name='null', address='null', vip=com.gongj.bean.User$Vip@2ef1e4fa}
vip:9
user2:User{name='null', address='null', vip=com.gongj.bean.User$Vip@2ef1e4fa}
user4:User{name='null', address='null', vip=com.gongj.bean.User$Vip@2ef1e4fa}
user2
user3
user4
user7
user5
user6
  • User对象
public class User {
    public User() {
    }

    public User(String name, String address) {
        this.name = name;
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", vip=" + vip +
                '}';
    }

    private String name;
    private String address;
    private Vip vip;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Vip getVip() {
        return vip;
    }

    public void setVip(Vip vip) {
        this.vip = vip;
    }


    // 内部Bean测试
    public class Vip {
        public String vipLevel;

        public String getVipLevel() {
            return vipLevel;
        }

        public void setVipLevel(String vipLevel) {
            this.vipLevel = vipLevel;
        }
    }
}
  • spring-config.xml
<?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  class="com.gongj.bean.User" name="user2,user5;user6,;user7" id="user">
        <property name="vip">
            <bean id="inner" class="com.gongj.bean.User$Vip">
                <constructor-arg ref="user"/>
                <property name="vipLevel" value="9"/>
            </bean>
        </property>
    </bean>
    <alias name="user2" alias="user3"></alias>
    <alias name="user2" alias="user4"></alias>

    <!--<beans profile="dev,;prd">
        <bean id="user" class="com.gongj.bean.User" name="user2,;user5"></bean>
    <alias name="user2" alias="user3"></alias>
    <alias name="user2" alias="user4"></alias>
    </beans>-->
</beans>
  • pom.xml
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>

接下来就开始我们的重头戏,XmlBeanDefinitionReader读取 xml 文件配置。


从我们写的这段代码进入 reader.loadBeanDefinitions("spring-config.xml"),上面已经提到最终会调用BeanDefinitionReader#loadBeanDefinitions(resource)方法,该方法的具体实现就交给了子类进行处理,而我们指定的子类实现为XmlBeanDefinitionReader,调试进入子类实现,可以看到它加载Resource并封装为EncodedResource,并指定编码,当然它的 encoding、 charset 都是null。

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return this.loadBeanDefinitions(new EncodedResource(resource)); >1
    }

 public EncodedResource(Resource resource) {
        this(resource, (String)null, (Charset)null);
    }

然后进入到loadBeanDefinitions(EncodedResource encodedResource) 方法,该方法主要根据encodedResource获得InputSource对象,并将InputSource对象参数和Resource对象参数传递给下层方法。

//当前正在加载的XML bean定义资源,使用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);
        }
        //获得当前正在加载的XML bean定义资源
        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 {
            //获取Resource对应的字节流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                //inputStream 流转为一个 InputSource 对象
                InputSource inputSource = new InputSource(inputStream);
                //如果资源有编 石马 格式,那就给 inputSource 对象也设置上编石马格式
                if (encodedResource.getEncoding() != null) {
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //该方法做进一步的解析操作,也是创建BeanDefinition的关键
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); >1
            }
            finally {
                inputStream.close();
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
            //移除当前正在加载的XML bean定义资源
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }

然后进入doLoadBeanDefinitions(InputSource inputSource, Resource resource) 方法,该方法主要是将xml 资源文件转换为Document对象并根据Dcoument对象注册BeanDefinition

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

        try {
            //将资源文件解析成 Document 对象
            Document doc = doLoadDocument(inputSource, resource); >1
           //根据返回的Dcoument注册Bean信息
            int count = registerBeanDefinitions(doc, resource); >2
            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);
        }
    }

我们先来看一下 doLoadDocument(InputSource inputSource, Resource resource)方法:

//定义从资源文件加载 到 转换为 Document 的功能
private DocumentLoader documentLoader = new DefaultDocumentLoader();

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware()); >1
    }

doLoadDocument 方法里最终调用的是 documentLoader.loadDocument ()方法。
该方法的调用,一共需要五个参数:

  • InputSource:要调用的资源文件。
  • EntityResolver: 处理文件的验证方式。
  • ErrorHandler: 错误处理器。
  • validationMode: XML 文件的验证模式。
  • namespaceAware: 是否开启自动感知名称空间。

documentLoader.loadDocument ()点击会进入到 DocumentLoader接口,该接口下只有一个实现:DefaultDocumentLoader
具体的调用如下:

@Override
    public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
        //创建DocumentBuilderFactory对象
        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); >1
        if (logger.isTraceEnabled()) {
            logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        //创建DocumentBuilder对象
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); >2
        //将inputSource解析为Document对象
        return builder.parse(inputSource);
    }

这里就会将 XML 里的配置解析为 Document对象。使用这个Document对象可以获取 XML 文件中的节点并且创建节点。

createDocumentBuilderFactory(validationMode, namespaceAware):创建DocumentBuilderFactory对象

protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
            throws ParserConfigurationException {
        // 1、获取DocumentBuilderFactory实例
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(namespaceAware);
        // 2、如果开启xml验证的话,则验证xml
        if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
            factory.setValidating(true);
            // 如果xml验证模式为XSD则需要强制指定由此代码生成的解析器将提供对XML名称空间的支持
            if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
                // Enforce namespace aware for XSD...
                factory.setNamespaceAware(true);
                try {
                    factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
                }
                catch (IllegalArgumentException ex) {
                    ParserConfigurationException pcex = new ParserConfigurationException(
                            "Unable to validate using XSD: Your JAXP provider [" + factory +
                            "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
                            "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
                    pcex.initCause(ex);
                    throw pcex;
                }
            }
        }
        return factory;
    }

createDocumentBuilder(factory, entityResolver, errorHandler):创建DocumentBuilder对象

protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
            @Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
            throws ParserConfigurationException {
        // 创建DocumentBuilder对象
        DocumentBuilder docBuilder = factory.newDocumentBuilder();
        // 如果entityResolver不为空,则给docBuilder设置entityResolver
        if (entityResolver != null) {
            docBuilder.setEntityResolver(entityResolver);
        }
        // 如果errorHandler不为空,则给docBuilder设置errorHandler
        if (errorHandler != null) {
            docBuilder.setErrorHandler(errorHandler);
        }
        return docBuilder;
    }

接下来我们看registerBeanDefinitions(doc, resource)方法

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 创建BeanDefinitionDocumentReader对象,完成 BeanDefinition 的解析和注册
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        //之前的bd数量
        int countBefore = getRegistry().getBeanDefinitionCount();
        // 注册bd
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); >1
        // 本次注册的bd数量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

这个方法中重点关注documentReader.registerBeanDefinitions(doc, createReaderContext(resource))方法。点击进入就会到 BeanDefinitionDocumentReader接口,该接口的作用就是定义读取 Docuemnt 并注册 BeanDefinition

该接口就一个实现类 DefaultBeanDefinitionDocumentReader,接下来就看 DefaultBeanDefinitionDocumentReader 类中的 registerBeanDefinitions 方法。

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

该方法有两个入参:

  • Document:代指 Spring 的配置文件信息,通过 BeanDefinitionReader 解析 Resrouce 实例得到。
  • XmlReaderContext :主要包含了 BeanDefinitionReaderResrouce

然后进入doRegisterBeanDefinitions(doc.getDocumentElement())方法,进行解析

protected void doRegisterBeanDefinitions(Element root) {
        // 创建 delegate 对象,解析 Element 的各种方法
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
        // 验证 XML 文件的命名空间,
        // 即判断是否含有 xmlns="http://www.springframework.org/schema/beans"
        if (this.delegate.isDefaultNamespace(root)) {
            // 获取profile属性的值  <beans profile="dev,;prd"></beans>
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                //将字符串按照指定的字符转换成String[]数组,如字符串中不包含指定字符,则将整个字符串放进数组。
                //如指定字符有多个,是分别按单个字符来切割的。
                //字符串: "gong-jie/yuan"
                //     *
                //     * 指定字符: "-/"
                //     * 返回数组:[gong, jie, yuan]
                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); >1
        // 后置空方法
        postProcessXml(root);
        this.delegate = parent;
    }

我们来看其中最重要的方法 parseBeanDefinitions(root, this.delegate);

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        //是否是DefaultNamespace
        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的命名规则,对该标签进行解析。
                    // 实例 <bean id="user" class="com.gongj.bean.User"></bean>
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate); >1
                    }
                    else {
                        // 解析用户自定义的规则
                        // <tx:annotation-driven/>
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
                // 解析用户自定义的规则   
            delegate.parseCustomElement(root);
        }
    }

而两种方式的读取及解析差别是非常大的,如果采用 Spring 默认的配置。Spring 当然知道该怎么做,但是如果是自定义的,那么就需要用户实现一些接口及配置,对于根节点或者子节点如果是默认命名空间的话,采用 parseDefaultElement 方法进行解析,否则使用delegate.parseCustomElement 方法对自定义命名空间进行解析。 而判断是否是默认命名空间还是自定义命名空间的办法其实是使用node.getNamespaceURI获取命名空间,并与 Spring 中固定的命名空间http://www.springframework.org/scherna/beans 进行比对,如果一致则认为是默认,否则就认为是自定义。


默认标签的解析是在 parseDefaultElement(ele, delegate) 函数进行的, 函数中的功能一目了然 ,分别对四种标签(import,alias,bean,beans)做了不同的处理。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        // 解析import标签  <import resource="spring-config.xml"></import>
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        // 解析alias标签 <alias name="user" alias="user2"></alias>
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        // 解析bean标签 <bean id="user" class="com.gongj.bean.User"></bean>
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate); >1
        }
        // 解析beans标签
        /**
         * <beans profile="dev">
         *         <bean id="user" class="com.gongj.bean.User"></bean>
         *         <import resource="spring-config.xml"></import>
         *         <alias name="user" alias="user2"></alias>
         *     </beans>
         */
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            //调用doRegisterBeanDefinitions,再次重复解析xml的过程
            doRegisterBeanDefinitions(ele);
        }
    }

BeanDefinitionReader 这个源码阅读就到这啦!下面将拆分几篇博文介绍各种标签的解析。


参考文献

Spring IoC之BeanDefinitionReader

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

18--Spring注册BeanDefinition

相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
15 2
|
1月前
|
XML Java 数据格式
Spring从入门到入土(xml配置文件的基础使用方式)
本文详细介绍了Spring框架中XML配置文件的使用方法,包括读取配置文件、创建带参数的构造对象、使用工厂方法和静态方法创建对象、对象生命周期管理以及单例和多例模式的测试。
83 7
Spring从入门到入土(xml配置文件的基础使用方式)
|
19天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
1月前
|
XML 前端开发 Java
讲解SSM的xml文件
本文详细介绍了SSM框架中的xml配置文件,包括springMVC.xml和applicationContext.xml,涉及组件扫描、数据源配置、事务管理、MyBatis集成以及Spring MVC的视图解析器配置。
57 1
|
8天前
|
Java Maven
maven项目的pom.xml文件常用标签使用介绍
第四届人文,智慧教育与服务管理国际学术会议(HWESM 2025) 2025 4th International Conference on Humanities, Wisdom Education and Service Management
55 8
|
9天前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
35 9
|
1月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
108 5
|
1月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
1月前
|
XML JavaScript Java
java与XML文件的读写
java与XML文件的读写
25 3
|
1月前
|
设计模式 JavaScript Java
Spring 事件监听机制源码
Spring 提供了事件发布订阅机制,广泛应用于项目中。本文介绍了如何通过自定义事件类、订阅类和发布类实现这一机制,并展示了如何监听 SpringBoot 启动过程中的多个事件(如 `ApplicationStartingEvent`、`ApplicationEnvironmentPreparedEvent` 等)。通过掌握这些事件,可以更好地理解 SpringBoot 的启动流程。示例代码展示了从事件发布到接收的完整过程。