spring源码分析(二)配置文件的解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 上一篇博客说明了下spring是如何找到资源文件的,classpath下的xml,最终会被解析为 ClassPathContextResource,下面进一步分析,有了这个资源文件之后spring是如何将其解析为BeanDefinition的入口XmlBeanDefinitionReader.

上一篇博客说明了下spring是如何找到资源文件的,classpath下的xml,最终会被解析为 ClassPathContextResource,下面进一步分析,有了这个资源文件之后spring是如何将其解析为BeanDefinition的

入口XmlBeanDefinitionReader.loadBeanDefinitions

最开始的入口,只是包了下Resource

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    //EncodedResource只是存了Resource的编码
    return loadBeanDefinitions(new EncodedResource(resource));
}

1)保存当前正在加载的资源
2)检测是否有重复加载资源的情况
3)真正干活的地方doLoadBeanDefinitions

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    
    //... 省略日志

    //1、保存当前正在加载的资源
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet<>(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }

    //2、检测是否有重复加载资源的情况
    if (!currentResources.add(encodedResource)) {
        throw new BeanDefinitionStoreException(
                "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }

    InputStream inputStream = encodedResource.getResource().getInputStream();
    try {
        //document加载需要的的InputSource
        InputSource inputSource = new InputSource(inputStream);
        if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
        }
        //3、真正干活的地方doLoadBeanDefinitions
        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
    }
    finally {
        inputStream.close();
    }

    //... 省略 一系列异常处理
}

1、这里做的两件事,将资源文件解析成Document
2、解析并注册BeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
        throws BeanDefinitionStoreException {
    
    Document doc = doLoadDocument(inputSource, resource);
    return registerBeanDefinitions(doc, resource);
    
}

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

document的解析

XmlBeanDefinitionReader 将 Document的生成,委托给了DocumentLoader

看下默认实现 DefaultDocumentLoader

@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
        ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

    //模板方法模式,子类继承DefaultDocumentLoader后可以替换DocumentBuilderFactory和DocumentBuilder的实现
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isDebugEnabled()) {
        logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    return builder.parse(inputSource);
}

这里使用了工厂方法模式


img_de2ee5fe42c0019072aef295b0d2529c.png

解析xml 并 注册BeanDefinition

有了document之后,就可以很方便的通过document的API来读取xml的元素。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    int countBefore = getRegistry().getBeanDefinitionCount();
    //beanDefininition的注册,委托给了BeanDefnitionDocumentReader
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    return getRegistry().getBeanDefinitionCount() - countBefore;
}
img_5a67e51d77ddc5ee6cf6f796bd2b6c62.png

具体的注册调用过程,就不跟了,总之,最后会调用到org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions方法

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

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // recurse
        doRegisterBeanDefinitions(ele);
    }
}

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

到了这一步,spring开始配置文件解析,具体如何解析,以及如何将配置文件封装为BeanDefinitions并进行注册,下一篇博客会分析。

目录
相关文章
|
8天前
|
负载均衡 算法 Java
Spring Cloud全解析:负载均衡算法
本文介绍了负载均衡的两种方式:集中式负载均衡和进程内负载均衡,以及常见的负载均衡算法,包括轮询、随机、源地址哈希、加权轮询、加权随机和最小连接数等方法,帮助读者更好地理解和应用负载均衡技术。
|
1月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
6天前
|
XML 监控 Java
Spring Cloud全解析:熔断之Hystrix简介
Hystrix 是由 Netflix 开源的延迟和容错库,用于提高分布式系统的弹性。它通过断路器模式、资源隔离、服务降级及限流等机制防止服务雪崩。Hystrix 基于命令模式,通过 `HystrixCommand` 封装对外部依赖的调用逻辑。断路器能在依赖服务故障时快速返回备选响应,避免长时间等待。此外,Hystrix 还提供了监控功能,能够实时监控运行指标和配置变化。依赖管理方面,可通过 `@EnableHystrix` 启用 Hystrix 支持,并配置全局或局部的降级策略。结合 Feign 可实现客户端的服务降级。
51 23
|
17天前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
15 0
Spring高手之路22——AOP切面类的封装与解析
|
19天前
|
Java 微服务 Spring
Spring Cloud全解析:配置中心之解决configserver单点问题
但是如果该configserver挂掉了,那就无法获取最新的配置了,微服务就出现了configserver的单点问题,那么如何避免configserver单点呢?
|
1月前
|
XML Java 数据格式
Spring Cloud全解析:注册中心之zookeeper注册中心
使用ZooKeeper作为Spring Cloud的注册中心无需单独部署服务器,直接利用ZooKeeper服务端功能。项目通过`spring-cloud-starter-zookeeper-discovery`依赖实现服务注册与发现。配置文件指定连接地址,如`localhost:2181`。启动应用后,服务自动注册到ZooKeeper的`/services`路径下,形成临时节点,包含服务实例信息。
127 3
|
1月前
|
安全 Unix Linux
samba 配置文件解析
【8月更文挑战第8天】Samba是一款开源软件套件,支持不同操作系统间的文件与打印机共享。它通过实现SMB/CIFS协议,确保了Linux/UNIX系统与Windows系统的互操作性。Samba具备文件共享、打印机管理、安全性设置及域控制器等功能,适用于搭建文件服务器、打印服务器及身份验证服务,并促进跨平台的数据访问与协作。其配置灵活,可根据需要调整共享目录的权限、可见性及用户认证方式。
29 3
|
17天前
|
Java Spring 传感器
AI 浪潮席卷,Spring 框架配置文件管理与环境感知,为软件稳定护航,你还在等什么?
【8月更文挑战第31天】在软件开发中,配置文件管理至关重要。Spring框架提供强大支持,便于应对不同环境需求,如电商项目的开发、测试与生产环境。它支持多种格式的配置文件(如properties和YAML),并能根据环境加载不同配置,如数据库连接信息。通过`@Profile`注解可指定特定环境下的配置生效,同时支持通过命令行参数或环境变量覆盖配置值,确保应用稳定性和可靠性。
31 0
|
19天前
|
消息中间件 Java RocketMQ
微服务架构师的福音:深度解析Spring Cloud RocketMQ,打造高可靠消息驱动系统的不二之选!
【8月更文挑战第29天】Spring Cloud RocketMQ结合了Spring Cloud生态与RocketMQ消息中间件的优势,简化了RocketMQ在微服务中的集成,使开发者能更专注业务逻辑。通过配置依赖和连接信息,可轻松搭建消息生产和消费流程,支持消息过滤、转换及分布式事务等功能,确保微服务间解耦的同时,提升了系统的稳定性和效率。掌握其应用,有助于构建复杂分布式系统。
33 0
|
19天前
|
机器学习/深度学习 计算机视觉 Python
深度学习项目中在yaml文件中定义配置,以及使用的python的PyYAML库包读取解析yaml配置文件
深度学习项目中在yaml文件中定义配置,以及使用的python的PyYAML库包读取解析yaml配置文件
31 0

推荐镜像

更多