• Spring IOC源码:obtainFreshBeanFactory 详解(下)


    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    方法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));
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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);
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    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());
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    方法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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

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

    自定义命名空间

    看完上面的步骤,大家应该对自定义命名空间过程有一定的理解,下面我们自定义案例来加深理解,我们需要按以下步骤来完成Demo。
    1、新建自定义命名空间处理器,一个处理器可以添加多个解析器
    2、新建自定义命名空间解析器,编写解析处理逻辑
    3、新建spring.handlers文件,按Key-Value格式指定处理器路径
    4、新建spring.schemas文件,按Key-Value格式指定xsd自定义标签文件路径
    5、新建xsd文件,声明标签规范,指定命名空间处理器
    6、新建xml文件配置自定义标签,完成类的管理。

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

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

    在这里插入图片描述编辑build.gradle文件,引入spring 包

    g)

    新建处理器类 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());
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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;
    
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    新建实体类

    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;
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    3、新建spring.handlers文件

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

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

    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
    
    • 1

    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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

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

    如果你的测试模块跟上述定义解析器标签不在同个模块下,记得先引入模块。
    在这里插入图片描述

    
    <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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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());
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试结果:
    在这里插入图片描述

    总结

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

  • 相关阅读:
    Pymoo:优化算法收敛性的实例分析
    Unity3d C#实现编辑器不运行状态下执行的脚本
    [论文阅读] 颜色迁移-Linear Monge-Kantorovitch(MKL)
    java-php-python-ssm运动场所预约管理网站计算机毕业设计
    MySQL注入绕安全狗脚本 -- MySQLByPassForSafeDog,以及端口爆破工具 -- PortBrute配置使用
    本周讨论用户体验:Daedalus 的 Nemo 加入 Ambire,探索加密海洋
    运营-用户分析
    KGAT推荐系统
    代码随想录二刷 Day 43
    Mysql索引(究极无敌细节版)
  • 原文地址:https://blog.csdn.net/weixin_45031612/article/details/127796059