首页 > 其他分享 >Spring如何j将xml配置映射为BeanDefinition

Spring如何j将xml配置映射为BeanDefinition

时间:2023-12-17 15:55:19浏览次数:32  
标签:xml beanFactory Spring BeanFactory ele delegate 解析 BeanDefinition

Spring的常用配置文件是applicationContext.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.harvey.demo.*"></context:component-scan>

</beans>

Spring是根据BeanDefinition来创建Bean对象,BeanDefinition就是Spring中表示Bean定义。BeanDefinition用来存储Bean的相关信息,主要包括:Bean的属性、是否单例、延迟加载、Bean的名称、构造方法等。

那Spring是如何找到所有的目标类创建对象的BeanDefinition的呢?

入口是AbstractApplicationContext的refresh方法

//-------- AbstractApplicationContext的refresh方法

// Prepare this context for refreshing.
prepareRefresh();

// Step2:调用子类覆盖的方法obtoinFreshBeanFactory(),创建新的BeanFactory,默认实现是DefaultListableBeanFactory
// 解析xml,生成BeanDefinition并注册到 BeanDefinitionRegistry
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();

在obtainFreshBeanFactory方法中一共是两个方法

  • refreshBeanFactory:初始化BeanFactory
  • getBeanFactory:返回实例化的BeanFactory
ConfigurableListableBeanFactory#obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}

走到refreshBeanFactory()方法内部,核心方法loadBeanDefinitions(),这个方法是读取xml文件读取bean的定义。最后赋值到该类的BeanFactory中。

AbstractRefreshableApplicationContext#refreshBeanFactory{
    //如果已经存在BeanFactory,则先销毁先前的BeanFactory,关闭连接以释放资源
    if (hasBeanFactory()) {
        //销毁先前的BeanFactory
        destroyBeans();
        //关闭连接
        closeBeanFactory();
    }
    try {
        //创建一个新的BeanFactory,在这里使用DefaultListableBeanFactory作为BeanFactory的默认实现类
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        //设置从ID,用于 BeanFactory 的序列化和反序列化
        beanFactory.setSerializationId(getId());
        //调用子类的customizeBeanFactory方法,用于在创建新的BeanFactony后,对BeanFactory进行附加的自定义设置
        customizeBeanFactory(beanFactory);
        /**    
         * 【重点】加载Bean定义,这个bean definition 是通过XmlBeanDefinitionReader来读取Spring的配置文件(通常为XL格式的〉,得到Bean定义实现类AbstractXmlApplicationContext    
         */
        loadBeanDefinitions(beanFactory);
        //将当前BeanFactory实例赋值给该类实例的beanFactory变量中
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        //如果在解析Bean定义源时遇到I/0错误,则抛出ApplicationContextException异常
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

 

 点击查看这个类下的实现方法。

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // 为beanFactory创建XmlBeanDefinitionReader对象
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    //用当前 ApplicationContext的环境来配置 BeanDefinition读取器
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

    //初始化 BeanDefinition 读取器,让子类可以对其进行自定义初始化(留一个可扩展的接口)
    initBeanDefinitionReader(beanDefinitionReader);
    /**关键方法:读取 BeanDefinition并向 BeanFactory注册*/
    loadBeanDefinitions(beanDefinitionReader);
}

然后走到loadBeanDefinitions(),还是调用loadBeanDefinitions()方法。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    Resource[] configResources = getConfigResources();
    if (configResources != null) {
        //将每一个Resource的 BeanDefinition 读取并注册到BeanDefinitionRegistry 中
        reader.loadBeanDefinitions(configResources);
    }
    //获取要加载的配置文件路径,即ClassPath下的XML配置
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
        /**
         * 关键方法:根据路径加载配置文件中的 BeanDefinition并注册到 BeanFactory 中
         */
        reader.loadBeanDefinitions(configLocations);
    }
}

 

 

 

@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
    Assert.notNull(resources, "Resource array must not be null");
    int counter = 0;
    for (Resource resource : resources) {
        /**入口,实现类XmlBeanDefinitionReader.loadBeanDefinitions*/
        counter += loadBeanDefinitions(resource);
    }
    return counter;
}
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.getResource());
    }

    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 {
        //把XML读取的文件流,解析成InputSource对象
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            /**入口,spring框架里面真正干活的方法名 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();
        }
    }
}

到了这个方法,才开始执行真正的逻辑,通过读取资源载入Bean定义信息。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    throws BeanDefinitionStoreException {
    try {
        //读取XML配置,把配置文件转成Document对象
        Document doc = doLoadDocument(inputSource, resource);
        /**根据解析得到的Document对象,将其中的 Bean配置信息注册到BeanFactory 中*/
        return registerBeanDefinitions(doc, resource);
    }
    catch (BeanDefinitionStoreException ex) {
        throw ex;
    }
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //创建 BeanDefinitionDocumentReader对象,用于读取文档转化成的 Element
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    //记录当前注册的 Bean数量
    int countBefore = getRegistry().getBeanDefinitionCount();
    /**
     * 通过BeanDefinitionDocumentReader的实现来对所提供的Document的BeanDefinition进行解析
     * 关键方法:registerBeanDefinitions
     */
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    //计算当前实际注册的Bean数量
    return getRegistry().getBeanDefinitionCount() - countBefore;
}
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    Element root = doc.getDocumentElement();
    doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
    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;
            }
        }
    }
    //预处理XNL元素,例如在XML解析之前可以修改XML DOM树,空实现,给子类定义
    preProcessXml(root);
    /**关键入口:解析Bean定义并注册 Bean 到 BeanFactory 容器中*/
    parseBeanDefinitions(root, this.delegate);
    //后处理XML元素,例如解析后的 Bean定义可能需要改变XNL DOM树等,空实现,给子类定义
    postProcessXml(root);
    // 恢复到原先的的 BeanDefinitionParserDelegate 委托对象
    this.delegate = parent;
}
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) {
                //如果子节点也是元素节点,并且也使用了默认命名空间,则通过默认方式解析元素
                //默认的命名空间是:http://www.springframework.org/schema/beans
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {
                    //解析bean元素信息
                    parseDefaultElement(ele, delegate);
                }
                else {
                    //如果没有指定默认命名空间,也通过自定义方式解析元素
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}

解析xml节点分为两种情况:

  • 解析默认命名空间下的元素,即http://www.springframework.org/schema/beans
  • 解析非默认命名空间下的元素

首先我们来看看如何解析默认命名空间下的元素

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)) {
        //关键方法:bean元素处理
        processBeanDefinition(ele, delegate);
    }
    //解析<beans>元素
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        //递归调用doRegisterBeanDefinitions()方法解析并注册嵌套的<beans>元素
        doRegisterBeanDefinitions(ele);
    }
}

 

 

 

回到前面代码,我们再看看是如何解析非默认命名空间下的元素的

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    //第一步:获取元素的namespaceUri
    String namespaceUri = getNamespaceURI(ele);
    if (namespaceUri == null) {
        return null;
    }
    //第二步:通过namespaceUri获取对应的NamespaceHandler,
    //例如<context>的命名空间处理器就是ContextNamespaceHandler
    //在这里会实例化NamespaceHandler,并调用其init方法,创建诸多解析器,其中就有component-scan对应的解析器ComponentScanBeanDefinitionParser
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
        error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
        return null;
    }
    //第三步:调用NamespaceHandler的parse方法准备开始解析
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
//【NamespaceHandlerSupport】
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
    //根据元素拿到对应的解析器,比如component-scan对应的解析器ComponentScanBeanDefinitionParser
    BeanDefinitionParser parser = findParserForElement(element, parserContext);
    //调用对应解析器的parse方法解析
    return (parser != null ? parser.parse(element, parserContext) : null);
}
//【ComponentScanBeanDefinitionParser】
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
    //获取元素的base-package属性值
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    //处理占位符
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
                                                              ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

    //根据一些自定义配置信息创建扫描器,比如include-filter、exclude-filter等等
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    //找到声明包路径下的组件,并将类信息封装,实例化为BeanDefinitionHolder,并注册到beanDefinitionMap中
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    //注册其他组件,比如一些依赖bean、内部bean
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    return null;
}

 整一个创建的流程,就走完啦,从解析xml,到封装成BeanDefinition,最后放入map中的核心流程。

 

标签:xml,beanFactory,Spring,BeanFactory,ele,delegate,解析,BeanDefinition
From: https://www.cnblogs.com/xfeiyun/p/17888325.html

相关文章

  • springboot连接mysql出现的SSL问题
    1、连接配置spring:datasource:username:rootpassword:rooturl:jdbc:mysql://192.168.0.1:3307/admin?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaidriver-class-name:com.mysql.cj.jdbc.Driver出现SSL连接问题2、解......
  • 最新Spring 6手写实现Sping IOC保姆级教程
    Java全能学习+面试指南:https://javaxiaobear.cn我们都知道,Spring框架的IOC是基于Java反射机制实现的,下面我们先回顾一下java反射。1、回顾Java反射Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种......
  • Java医院3D人体智能导诊系统源码 Uniapp+springboot
    “智能导诊”以人工智能手段为依托,为人们提供智能分诊、问病信息等服务,在一定程度上满足了人们自我健康管理、精准挂号等需求。智能导诊可根据描述的部位和病症,给出适合病症的科室参考。智慧导诊页面会显示男性或女性的身体结构图,可切换正面/背面。通过点击部位选项,选择自己身体不......
  • Spring的BeanDefinition是什么
    BeanDefinition是什么?在Spring框架中,BeanDefinition是描述和定义Spring容器中的Bean的元数据对象。它包含了定义Bean的相关信息,例如Bean的类名、作用域、生命周期等。BeanDefinition对象通常由Spring容器在启动过程中根据配置信息或注解生成。是SpingIoc容器管理的核心数据结构......
  • Spring Boot Actuator 使用和常用配置
    转载请注明出处:SpringBootActuator是SpringBoot提供的一个非常强大的工具,它可以帮助我们监控和管理我们的SpringBoot应用。Actuator提供了一系列的端点,我们可以通过这些端点来查看我们的应用的运行状态,例如健康状态、指标、信息、数据库连接、Redis连接等等1.配置Spring......
  • SpringBoot集成Swagger的使用
    一、前言Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。目标是使客户端和文件系统作为服务器以同样的速度来更新文件的方法,参数和模型紧密集成到服务器。Swagger能够在线自动生成RESTFul接口的文档,同时具备测试接口的功能。简单点来讲就是说......
  • SpringBoot集成多个RabbitMq(多个MQ链接)
    ##2023年12月16日20:25:36 项目中使用RabbitMQ作为应用间信息互通,本次梳理下关于MQ的使用。1、引入依赖<!--引入依赖,使用v2.5.6版本--><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-b......
  • 第八章:SpringBoot-Java工程及关系和修改启动logo(了解)
    一、springbootJava工程(了解)二、关闭&修改启动logo(了解)......
  • Springboot整合MybatisPlus
    1、引入mybatis-plus坐标<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.2.0</version></dependency>2、配置数据源spring:datasource:use......
  • 第五章:SpringMVC的常用注解
    一、21、springMVC常用注解二、springMVC相关注解理论整合1、@RequestMapping:用来处理请求地址映射的注解,可用于类或方法上@RequestMapping("/path")表示该控制器处理所有"/path"的URL请求。用于类上,表示类中的所有响应请求的方法都是以该地址作......