摘要:在上一篇文章中,分析了容器的创建,加载资源文件,将资源文件读取为。将文件中的注册定义的对象。在中对属性的解析委托给这个代理类来实现的。首先,获取节点。
在上一篇文章中,分析了ApplicationContext容器的创建,加载资源文件,将资源文件读取为Document。spring将xml文件中的Bean注册spring定义的BeanDefinition对象。在DefaultBeanDefinitionDocumentReader中对Document属性的解析委托给BeanDefinitionParserDelegate这个代理类来实现的。
Bean注册前的准备DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法实现如下,首先获取Document的根元素,接着调用doRegisterBeanDefinitions(root)进行注册
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; //获取根元素 Element root = doc.getDocumentElement(); //注册BeanDefinition doRegisterBeanDefinitions(root); }
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); //是否有profile属性 if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } } preProcessXml(root); //解析BeanDefinition parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }根据不同节点名进行解析
parseBeanDefinitions(root, this.delegate)方法是解析根源素下定义的每一个bean。首先,获取节点List。其次,判断每个元素是否为默认的命名空间中的元素,然后交给不同的方法去解析,具体的实现如下:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //如果根元素为默认命名空间中的元素 if (delegate.isDefaultNamespace(root)) { //获取字元素List 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); } }spring默认命名空间节点的解析
下面,我们首先看spring默认命名空间元素的解析过程,parseDefaultElement(ele, delegate)方法的实现如下:
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); } }
import节点的解析
import元素是引入其他的配置文件,resource属性是配置文件的路径,importBeanDefinitionResource(ele)方法的实现如下,省略了异常处理代码:
protected void importBeanDefinitionResource(Element ele) { //获取resource属性值,即其他配置文件的路径 String location = ele.getAttribute(RESOURCE_ATTRIBUTE); // 解析路径,如"${user.dir}" 这样的路径是从在propertie文件中加载的 location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); SetactualResources = new LinkedHashSet<>(4); // 判断location 是绝对路径还是相对路径 boolean absoluteLocation = false; absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); // 绝对路径 if (absoluteLocation) { //调用loadBeanDefinitions(location, actualResources)方法解析此配置文件 int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); } } //相对路径 else { int importCount; Resource relativeResource = getReaderContext().getResource().createRelative(location); if (relativeResource.exists()) { //调用loadBeanDefinitions(relativeResource)方法解析此配置文件 importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } else { String baseLocation = getReaderContext().getResource().getURL().toString(); importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); } } } //广播Import元素处理事件 Resource[] actResArray = actualResources.toArray(new Resource[0]); getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); }
alias节点的解析
下面介绍alias元素的方法,processAliasRegistration(ele)方法的实现如下:
protected void processAliasRegistration(Element ele) { //获取name属性 String name = ele.getAttribute(NAME_ATTRIBUTE); //获取alias属性 String alias = ele.getAttribute(ALIAS_ATTRIBUTE); boolean valid = true; if (!StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele); valid = false; } if (!StringUtils.hasText(alias)) { getReaderContext().error("Alias must not be empty", ele); valid = false; } if (valid) { //调用SimpleAliasRegistry类的registerAlias(name, alias)进行注册 getReaderContext().getRegistry().registerAlias(name, alias); getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } }
在SimpleAliasRegistry中定义了aliasMap来存储alias和name的关系,具体实现如下:
private final MapaliasMap = new ConcurrentHashMap<>(16); public void registerAlias(String name, String alias) { Assert.hasText(name, ""name" must not be empty"); Assert.hasText(alias, ""alias" must not be empty"); synchronized (this.aliasMap) { //如果alias和name相等,将此关系移除 if (alias.equals(name)) { this.aliasMap.remove(alias); } else { //先从aliasMap获取key为alias的beanName String registeredName = this.aliasMap.get(alias); if (registeredName != null) { //如果已存在,return if (registeredName.equals(name)) { // An existing alias - no need to re-register return; } //如果不存在,判断alias是否可以继承,默认是true if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot register alias "" + alias + "" for name "" + name + "": It is already registered for name "" + registeredName + ""."); } } //检查是否存在循环依赖 checkForAliasCircle(name, alias); //注册alias和name this.aliasMap.put(alias, name); } } }
bean节点的解析
processBeanDefinition(ele, delegate)实现如下:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //解析bean元素,创建BeanDefinitionHolder实例 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { //完成必须的装配 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 进行最终的注册bean BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name "" + bdHolder.getBeanName() + """, ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
bean元素的解析和注册相对复杂,在下一节中讨论。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/76388.html
摘要:在上一篇文章中,分析了容器的创建,加载资源文件,将资源文件读取为。将文件中的注册定义的对象。在中对属性的解析委托给这个代理类来实现的。首先,获取节点。 在上一篇文章中,分析了ApplicationContext容器的创建,加载资源文件,将资源文件读取为Document。spring将xml文件中的Bean注册spring定义的BeanDefinition对象。在DefaultBeanD...
摘要:在上一篇文章中,分析了容器的创建,加载资源文件,将资源文件读取为。将文件中的注册定义的对象。在中对属性的解析委托给这个代理类来实现的。首先,获取节点。 在上一篇文章中,分析了ApplicationContext容器的创建,加载资源文件,将资源文件读取为Document。spring将xml文件中的Bean注册spring定义的BeanDefinition对象。在DefaultBeanD...
摘要:在的方法中,遍历每一个节点,判断是否为默认命名空间中的节点,如果是非默认命名空间的,调用方法进行处理。在学习自定义标签解析之前,先写一个自定义标签的。 在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍历每一...
摘要:在的方法中,遍历每一个节点,判断是否为默认命名空间中的节点,如果是非默认命名空间的,调用方法进行处理。在学习自定义标签解析之前,先写一个自定义标签的。 在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍历每一...
摘要:在的方法中,遍历每一个节点,判断是否为默认命名空间中的节点,如果是非默认命名空间的,调用方法进行处理。在学习自定义标签解析之前,先写一个自定义标签的。 在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍历每一...
阅读 1369·2021-10-13 09:39
阅读 1333·2021-09-23 11:22
阅读 2242·2019-08-30 14:05
阅读 1059·2019-08-29 17:03
阅读 770·2019-08-29 16:24
阅读 2227·2019-08-29 13:51
阅读 655·2019-08-29 13:00
阅读 1290·2019-08-29 11:24