Spring IOC源码:obtainFreshBeanFactory 详解(下)

简介: Spring IOC源码:obtainFreshBeanFactory 详解(下)

文章目录

Spring源码系列:

前言

正文

方法1:parseCustomElement

方法2:getNamespaceHandlerResolver().resolve

方法3:getHandlerMappings

方法4:namespaceHandler.init()

方法5:handler.parse

自定义命名空间

1、新建自定义命名空间处理器

2、新建自定义命名空间解析器

3、新建spring.handlers文件

4、新建spring.schemas文件

5、新建xsd文件,声明标签规范

6、新建xml文件配置自定义标签

6、测试用例

总结

Spring源码系列:

Spring IOC源码:简单易懂的Spring IOC 思路介绍

Spring IOC源码:核心流程介绍

Spring IOC源码:ApplicationContext刷新前准备工作

Spring IOC源码:obtainFreshBeanFactory 详解(上)

Spring IOC源码:obtainFreshBeanFactory 详解(中)

Spring IOC源码:obtainFreshBeanFactory 详解(下)

Spring IOC源码:<context:component-scan>源码详解

Spring IOC源码:invokeBeanFactoryPostProcessors 后置处理器详解

Spring IOC源码:registerBeanPostProcessors 详解

Spring IOC源码:实例化前的准备工作

Spring IOC源码:finishBeanFactoryInitialization详解

Spring IoC源码:getBean 详解

Spring IoC源码:createBean( 上)

Spring IoC源码:createBean( 中)

Spring IoC源码:createBean( 下)

Spring IoC源码:finishRefresh 完成刷新详解

前言

前面两篇文章介绍了BeanDefinition的解析,以及默认命名空间的解析过程,这节介绍obtainFreshBeanFactory中另一个核心的解析逻辑,自定义命名空间的解析。

正文

自定义命名空间解析,入口方法为delegate.parseCustomElement(ele),见方法1详解

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

方法1:parseCustomElement

  public BeanDefinition parseCustomElement(Element ele) {
    return parseCustomElement(ele, null);
  }
  /**
   * Parse a custom element (outside of the default namespace).
   * @param ele the element to parse
   * @param containingBd the containing bean definition (if any)
   * @return the resulting bean definition
   */
  @Nullable
  public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    //获取命名空间路径 例如:http://www.springframework.org/schema/aop
    String namespaceUri = getNamespaceURI(ele);
    if (namespaceUri == null) {
      return null;
    }
    //通过命名空间解析器加载命名空间处理器
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
      error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
      return null;
    }
    //调用解析
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
  }

this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)方法,见方法3详解

handler.parse(ele, new ParserContext(this.readerContext, this, containingBd))方法,见方法5详解

方法2:getNamespaceHandlerResolver().resolve

  public NamespaceHandler resolve(String namespaceUri) {
    //获取命名空间集合
    Map<String, Object> handlerMappings = getHandlerMappings();
    //根据命名空间路径获取
    Object handlerOrClassName = handlerMappings.get(namespaceUri);
    if (handlerOrClassName == null) {
      return null;
    }
    else if (handlerOrClassName instanceof NamespaceHandler) {
      return (NamespaceHandler) handlerOrClassName;
    }
    else {
      //如果是未实例化的类路径,转为String
      String className = (String) handlerOrClassName;
      try {
        Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
        if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
          throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
              "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
        }
        //通过反射创建对象
        NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
        //调用处理器初始化方法
        namespaceHandler.init();
        //加入集合缓存中
        handlerMappings.put(namespaceUri, namespaceHandler);
        return namespaceHandler;
      }
      catch (ClassNotFoundException ex) {
        throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
            "] for namespace [" + namespaceUri + "]", ex);
      }
      catch (LinkageError err) {
        throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
            className + "] for namespace [" + namespaceUri + "]", err);
      }
    }
  }

getHandlerMappings(),见方法3详解

namespaceHandler.init(),见方法4详解

方法3:getHandlerMappings

private Map<String, Object> getHandlerMappings() {
    //获取当前集合中的处理器
    Map<String, Object> handlerMappings = this.handlerMappings;
    if (handlerMappings == null) {
      synchronized (this) {
        handlerMappings = this.handlerMappings;
        if (handlerMappings == null) {
          if (logger.isTraceEnabled()) {
            logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
          }
          try {
            //根据处理器路径进行查找加载
            Properties mappings =
                PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
            if (logger.isTraceEnabled()) {
              logger.trace("Loaded NamespaceHandler mappings: " + mappings);
            }
            handlerMappings = new ConcurrentHashMap<>(mappings.size());
            CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
            this.handlerMappings = handlerMappings;
          }
          catch (IOException ex) {
            throw new IllegalStateException(
                "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
          }
        }
      }
    }
    return handlerMappings;
  }

handlerMappings集合中的处理器如下图:

方法4:namespaceHandler.init()

因为当前配置文件中的自定义命名空间是AOP,所以这里会进入AOP处理器的init方法。

  public void init() {
    //这里主要注册需要用到的解析器
    // In 2.0 XSD as well as in 2.1 XSD.
    registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
    registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
    registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
    // Only in 2.0 XSD: moved to context namespace as of 2.1
    registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
  }

方法5:handler.parse

进入方法1中的handler.parse(ele, new ParserContext(this.readerContext, this, containingBd))方法;

  public BeanDefinition parse(Element element, ParserContext parserContext) {
    //根据当前节点的标签去查找对应的解析器
    BeanDefinitionParser parser = findParserForElement(element, parserContext);
    //调用解析器处理逻辑
    return (parser != null ? parser.parse(element, parserContext) : null);
  }
  /**
   * Locates the {@link BeanDefinitionParser} from the register implementations using
   * the local name of the supplied {@link Element}.
   */
  @Nullable
  private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    //获取节点标签  如:config
    String localName = parserContext.getDelegate().getLocalName(element);
    //根据名称获取解析器
    BeanDefinitionParser parser = this.parsers.get(localName);
    if (parser == null) {
      parserContext.getReaderContext().fatal(
          "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    }
    return parser;
  }

到此整个obtainFreshBeanFactory 的讲解就结束了,自定义命名空间处理器及其解析方法,后续文章中会跟案例进行讲解。

自定义命名空间

看完上面的步骤,大家应该对自定义命名空间过程有一定的理解,下面我们自定义案例来加深理解,我们需要按以下步骤来完成Demo。

1、新建自定义命名空间处理器,一个处理器可以添加多个解析器

2、新建自定义命名空间解析器,编写解析处理逻辑

3、新建spring.handlers文件,按Key-Value格式指定处理器路径

4、新建spring.schemas文件,按Key-Value格式指定xsd自定义标签文件路径

5、新建xsd文件,声明标签规范,指定命名空间处理器

6、新建xml文件配置自定义标签,完成类的管理。

1、新建自定义命名空间处理器

我们新建一个空模块Gradle空模块

编辑build.gradle文件,引入spring 包

新建处理器类 ZdcHandler

package com.zhudachang.namespace;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class ZdcHandler extends NamespaceHandlerSupport {
  @Override
  public void init() {
    super.registerBeanDefinitionParser("zdc",new ZdcParser());
  }
}

2、新建自定义命名空间解析器

package com.zhudachang.namespace;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
/**
 * Zdc解析器
 */
public class ZdcParser implements BeanDefinitionParser {
  @Override
  public BeanDefinition parse(Element element, ParserContext parserContext) {
    RootBeanDefinition beanDefinition = new RootBeanDefinition();
    beanDefinition.setBeanClass(ZdcDomain.class);
    //声明PropertyValues对象
    MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues();
    // 添加name属性
    if (element.hasAttribute("name")) {
      mutablePropertyValues.addPropertyValue("name", element.getAttribute("name"));
    }
    // 解析添加remark属性
    if (element.hasAttribute("remark")) {
      mutablePropertyValues.addPropertyValue("remark", element.getAttribute("remark"));
    }
    String id = element.getAttribute("id");
    // 拿到注册表, 注册BeanDefinition
    parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
    return beanDefinition;
  }
}

新建实体类

package com.zhudachang.namespace;
public class ZdcDomain {
  String name;
  String remark;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getRemark() {
    return remark;
  }
  public void setRemark(String remark) {
    this.remark = remark;
  }
}

3、新建spring.handlers文件

resource目录下创建META-INF目录,并创建spring.handlers文件,指定空间路径及其对应的处理器类路径

http\://com.zhudachang.namespace/schema/zdc=com.zhudachang.namespace.ZdcHandler

4、新建spring.schemas文件

resource目录下创建META-INF目录,并创建spring.schemas文件,指定路径及其对应xsd文件路径

http\://com.zhudachang.namespace/schema/zdc/zdc-1.0.xsd=./com/zdc/spancename/zdc/config/zdc-1.0.xsd

5、新建xsd文件,声明标签规范

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://com.zhudachang.namespace/schema/zdc"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      targetNamespace="http://com.zhudachang.namespace/schema/zdc"
      elementFormDefault="qualified">
  <xsd:element name="zdc" >
    <xsd:complexType>
      <xsd:attribute name="id" type="xsd:string"/>
      <xsd:attribute name="name" type="xsd:string"/>
      <xsd:attribute name="remark" type="xsd:string"/>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

6、新建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:zdc="http://com.zhudachang.namespace/schema/zdc"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://com.zhudachang.namespace/schema/zdc
       http://com.zhudachang.namespace/schema/zdc/zdc-1.0.xsd">
    <zdc:zdc id="zdc" name="猪大肠" remark="猪大肠备注"></zdc:zdc>
</beans>

6、测试用例

编写测试用例

  public static void main(String[] args) {
    ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("application-namespace.xml");
    ZdcDomain zdcDomain = (ZdcDomain) applicationContext.getBean("zdc");
    System.out.println(zdcDomain.getName());
  }

测试结果:

总结

本篇文章介绍了自定义命名空间的解析过程,并且编写的自定义命名空间案例加深理解。后续文章中会拿常用的自定义标签context:component-scan 进行讲解。

目录
相关文章
|
2月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
4月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
8月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
535 70
|
6月前
|
XML 人工智能 Java
Spring IOC 到底是什么?
IOC(控制反转)是一种设计思想,主要用于解耦代码,简化依赖管理。其核心是将对象的创建和管理交给容器处理,而非由程序直接硬编码实现。通过IOC,开发者无需手动new对象,而是由框架负责实例化、装配和管理依赖对象。常见应用如Spring框架中的BeanFactory和ApplicationContext,它们实现了依赖注入和动态管理功能,提升了代码的灵活性与可维护性。
198 1
|
XML Java 数据格式
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
京东一面:spring ioc容器本质是什么? ioc容器启动的步骤有哪些?
|
7月前
|
XML Java 数据格式
Spring IoC容器的设计与实现
Spring 是一个功能强大且模块化的 Java 开发框架,其核心架构围绕 IoC 容器、AOP、数据访问与集成、Web 层支持等展开。其中,`BeanFactory` 和 `ApplicationContext` 是 Spring 容器的核心组件,分别定位为基础容器和高级容器,前者提供轻量级的 Bean 管理,后者扩展了事件发布、国际化等功能。
|
Java 关系型数据库 数据库连接
Spring源码解析--深入Spring事务原理
本文将带领大家领略Spring事务的风采,Spring事务是我们在日常开发中经常会遇到的,也是各种大小面试中的高频题,希望通过本文,能让大家对Spring事务有个深入的了解,无论开发还是面试,都不会让Spring事务成为拦路虎。
332 1
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
524 5
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
256 4

热门文章

最新文章