spring默认标签解析及注册实现

一,前言

在使用spring的时候,会进场使用到beanbeans,import,aop等标签。
下面将介绍spring的默认的自带标签解析流程。

二,XML的验证模式(DTD&XSD)

XML文件的验证模式保证了XML文件的正确性,而比较常用的验证模式有两种:DTD和XSD。

  • DTD(Document Type Definition)即文档类型定义,是一种XML约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。DTD是一种保证XML文档格式正确的有效方法,可以通过比较XML文档和DTD文件来看文档是否符合规范,元素和标签使用是否正确。

一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则
要使用DTD验证模式的时候需要在XML文件的头部声明,以下是使用DTD声明方式的代码:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
  • XML Schema语言就是XSD(XML Schemas Definition)。XML Schema描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,以检查该XML文档是否符合其要求。文档设计者可以通过XML Schema指定一个XML文档所允许的结构和内容,并可据此检查一个XML文档是否是有效的。XML Schema本身是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析它。

在使用XML Schema文档对XML实例文档进行检验,除了要声明名称空间外(xmlns= http://www.springframework.org/schema/beans),还必须指定该名称空间所对应的XML Schema文档的存储位置。通过schemaLocation属性来指定名称空间所对应的XML Schema文档的存储位置,它包含两个部分,一部分是名称空间的URI,另一部分就是该名称空间所标识的XML Schema文件位置或URL地址(xsi:schemaLocation="http://www.springframework.org/schema/beans http://www. springframework.org/schema/beans/spring-beans.xsd)。

DTD基本已被淘汰,现在spring的验证模式基本都是采用xsd文件作为xml文档的验证模式,通过xsd文件可以检查该xml是否符合规范,是否有效。

三,名称空间URI

当我们使用spring标签的时候,例如使用aop标签的时候,这个aop标签是在哪里解析的呢,例如看下面的例子:

    <?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:aop="http://www.springframework.org/schema/aop"  
    	xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd  ">
            
     	<aop:aspectj-autoproxy/>  
        <bean name="purchaseService"   class="net.itaem.aop.PurchaseService" />
        <bean name="purchaseService2"   class="net.itaem.aop.fPurchaseService2" />
        <bean class="net.itaem.aop.LogAspect"/>    
    </beans>

其中schemaLocation有包含http://www.springframework.org/schema/aop这个地址,我们可以在这里找到
在这里插入图片描述
我们可以看到,它对应着一个类的路径,是AopNamespaceHandler,这代表着aop的实现是经过这个处理器实现的。

四,XSD文件位置

我们可以看到在xsi:schemaLocation里面http://www.springframework.org/schema/aop 后面还跟着http://www.springframework.org/schema/aop/spring-aop-3.2.xsd,这个xsd文件代表着aop标签的定义以及解析校验规范都是在这里定义的。那么是不是要去这个网址http://www.springframework.org/schema/aop/spring-aop-3.2.xsd下载呢,让我们先看看下面这个截图:
在这里插入图片描述
从中可以看到https\://www.springframework.org/schema/aop/spring-aop-4.3.xsd对应着一个路径,在这个路径我们可以找到对应的xsd文件,所以,在解析该标签的时候spring会根据schemas文件下面对应的路径去找这个校验文件,如果实在是没有找到的话它才回去网上下载然后再执行校验。

五,确定XML的解析模式

spring的配置文件已经写好了,spring启动的时候,它究竟是如何确定spring使用的是哪种xml验证模式呢,是DTD还是XSD,下面我们先通过时序图看看spring在判断验证模式的实现

在这里插入图片描述
绕了那么多个方法,我们终于看到获取xml验证模式的获取了。

    	protected int getValidationModeForResource(Resource resource) {
    
    
    		int validationModeToUse = getValidationMode();
    //如果是手动指定了验证模式,那么就使用手动指定的验证模式
    		if (validationModeToUse != VALIDATION_AUTO) {
    
    
    			return validationModeToUse;
    		}
    //如果未指定,那么就自动检测验证模式
    		int detectedMode = detectValidationMode(resource);
    		if (detectedMode != VALIDATION_AUTO) {
    
    
    			return detectedMode;
    		}
    		// Hmm, we didn't get a clear indication... Let's assume XSD,
    		// since apparently no DTD declaration has been found up until
    		// detection stopped (before finding the document's root tag).
    		return VALIDATION_XSD;
    	}

一般的话,我们是不会手动去指定spring的验证模式的,所以的话通常是spring自动检测xml的验证模式,那么通过什么检测,怎么实现呢,detectValidationMode方法是怎么判断的呢,其实判断很简单,就是判断xml文件开始时候的定义是否包含文档类型定义(doctype),如果包含,那么就是dtd的文档验证模式,如果不包含,那么就是xsd的验证模式。

六,加载xml文档

确定好,文档的解析模式后,下面的话便调用loadDocument方法对xml文档进行加载。对于xml文档的加载spring采用的还是javax.xml.parsers.DocumentBuilderFactory下面的DocumentBuilder去解析inputsource,返回document对象。

那么解析的时候是怎么获取验证文件的呢。在文章的前面有说过如果本地有验证文件那么就从本地获取验证文件,如果本地没有验证文件才去网上下载验证文件,那么这是怎么实现的呢?

下面这小部分内容可以看这篇博客,加深了解

其实这关键就在于EntityResolver这个类。何为EntityResolver,官方是这样解释的:如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口并使用setEntityResolver方法向SAX驱动器注册一个实例,也就是说对于解析一个xmlSAX首先读取该XML文档的声明,根据声明去找相应的DTD或者XSD定义,然后根据DTD或者XSD对文档进行一个验证。

在spring里面用DelegatingEntityResolver实现了EntityResolver类,并在解析的时候会根据要获取的解析文件URL先去本地找对应的验证文件,DTD文件是在当前路径下去找,XSD文件统一是从META-INF/Spring.schemas文件中去找对应的本都验证文件。

七,解析并注册BeanDefinitions

让我们继续doLoadBeanDefinitions方法的执行,先看看解析注册beandefinitions的时序图

在这里插入图片描述

  • spring有4种不同的默认标签,分别是import,alias,bean,beans,这四种标签的解析统一是由parseDefaultElement方法完成。
  • 自定义的标签,像aop标签,是由parseCustomElement方法完成的。

八,默认标签解析

让我们来先看看spring是如何解析默认的标签的,先看parseDefaultElement方法的内容:

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

从上面可以看到parseDefaultElement方法包含了spring 4中不同的默认标签的解析入口,针对这4种标签的解析大同小异,但是其中最复杂的应该属bean标签,因为他的属性最多,所逻辑也就比较复杂一点,下面我们通过bean标签的解析流程来看看spring是如何解析默认标签的。

先上DefaultBeanDefinitionDocumentReader类的processBeanDefinition方法:

  1. 首先委托BeanDefinitionParserDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的示例bdHolder,经过这个方法后,bdHolder示例已经包含我们配置文件中配置的各个属性了,如class、name、id、alias之类的属性。
  2. 当返回的bdHolder不为空的情况下,若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析,也就是调用decorateBeanDefinitionIfRequired方法
  3. 解析完成后,需要对解析之后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法。
  4. 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了。

下面让我看看parseBeanDefinitionElement方法的实现:

    	/**
   	 * Parses the supplied {@code <bean>} element. May return {@code null}
   	 * if there were errors during parse. Errors are reported to the
   	 * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
   	 */
   	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
    
    
   		//获取id属性
   		id = ele.getAttribute(ID_ATTRIBUTE);
   		//获取name属性
   		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
   		//分割name属性
   		List<String> aliases = new ArrayList<String>();
   		if (StringUtils.hasLength(nameAttr)) {
    
    
   			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
   			aliases.addAll(Arrays.asList(nameArr));
   		}
    
   		String beanName = id;
   		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
    
    
   			beanName = aliases.remove(0);
   			if (logger.isDebugEnabled()) {
    
    
   				logger.debug("No XML 'id' specified - using '" + beanName +
   						"' as bean name and " + aliases + " as aliases");
   			}
   		}
    
   		if (containingBean == null) {
    
    
   			checkNameUniqueness(beanName, aliases, ele);
   		}
   		//解析bean的属性和元素,并分装成GeneicBeanDefinition中(GeneicBeanDefinition是AbstractBeanDefinition实现类)
   		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
   		if (beanDefinition != null) {
    
    
   			if (!StringUtils.hasText(beanName)) {
    
    
   			//如果不存在beanName,则使用默认的规则创建beanName
   				try {
    
    
   					if (containingBean != null) {
    
    
   						beanName = BeanDefinitionReaderUtils.generateBeanName(
   								beanDefinition, this.readerContext.getRegistry(), true);
   					}
   					else {
    
    
   						beanName = this.readerContext.generateBeanName(beanDefinition);
   						// Register an alias for the plain bean class name, if still possible,
   						// if the generator returned the class name plus a suffix.
   						// This is expected for Spring 1.2/2.0 backwards compatibility.
   						String beanClassName = beanDefinition.getBeanClassName();
   						if (beanClassName != null &&
   								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
   								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
    
    
   							aliases.add(beanClassName);
   						}
   					}
   					if (logger.isDebugEnabled()) {
    
    
   						logger.debug("Neither XML 'id' nor 'name' specified - " +
   								"using generated bean name [" + beanName + "]");
   					}
   				}
   				catch (Exception ex) {
    
    
   					error(ex.getMessage(), ele);
   					return null;
   				}
   			}
   			String[] aliasesArray = StringUtils.toStringArray(aliases);
   			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
   		}
    
   		return null;
   	}

解析的核心逻辑由parseBeanDefinitionElement方法实现,让我们看看parseBeanDefinitionElement方法到底为我们做了哪些操作。

  1. 提取元素中的id和name属性
  2. 进一步解析bean中其他所有的属性,这些属性包含scope、singleton、abstract、lazy-init、autowire、dependency-check、depends-on、autowire-candidate、primary、init-method、destroy-method、factory-method、factory-bean。并且解析bean下面所有的元素,其中包含:meta、lookup-method、replace-method、constructor-arg、property、qualifier。解析完成这些属性和元素之后(元素和属性很多,所以这是一个庞大的工作量),统一封装到GeneicBeanDefinition类型的实例中。
  3. 如果检测到bean没有指定的beanName,那么便使用默认的规则为bean生成一个beanName
  4. 将获取到的信息封装到BeanDeinitionHolder里面去

九,自定义标签的解析

观看自定义标签的解析内容时,建议先看这篇博客自定义标签实现及使用

1,解析总流程

在这里插入图片描述

2,自定义标签解析入口

让我们先看BeanDefinitionParserDelegate类的parseCustomElement方法:

    	public BeanDefinition parseCustomElement(Element ele) {
    
    
    		return parseCustomElement(ele, null);
    	}
    	//containingBd为父类bean,对顶层元素的解析应设置为null
    	public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    
    
    		String namespaceUri = getNamespaceURI(ele);
    		//根据命名空间找到对应的NamespaceHandler进行解析
    		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进行解析
    		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    	}

3,提取自定义标签处理器

跟踪this.readerContext.getNamespaceHandlerResolver(). resolve(namespaceUri);
进入DefaultNamespaceHandlerResolver中的resolve方法

    	/**
    	 * Locate the {@link NamespaceHandler} for the supplied namespace URI
    	 * from the configured mappings.
    	 * @param namespaceUri the relevant namespace URI
    	 * @return the located {@link NamespaceHandler}, or {@code null} if none found
    	 */
    	public NamespaceHandler resolve(String namespaceUri) {
    
    
    		//获取所有已经配置的handler映射
    		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 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的初始化方法
    				namespaceHandler.init();
    				//记录在缓存
    				handlerMappings.put(namespaceUri, namespaceHandler);
    				return namespaceHandler;
    			}
    			catch (ClassNotFoundException ex) {
    
    
    				throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
    						namespaceUri + "] not found", ex);
    			}
    			catch (LinkageError err) {
    
    
    				throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
    						namespaceUri + "]: problem with handler class file or dependent class", err);
    			}
    		}
    	}

让我们在看看spring是如何获取handler映射的,查看getHandlerMappings()方法



    /**
    	 * Load the specified NamespaceHandler mappings lazily.
    	 */
    	private Map<String, Object> getHandlerMappings() {
    
    
    		//如果没有被缓存,则开始进入缓存
    		if (this.handlerMappings == null) {
    
    
    			synchronized (this) {
    
    
    				if (this.handlerMappings == null) {
    
    
    					try {
    
    
    						//this.handlerMappings在构造函数已被初始化为META-INF/Spring.handlers
    						Properties mappings =
    								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
    						if (logger.isDebugEnabled()) {
    
    
    							logger.debug("Loaded NamespaceHandler mappings: " + mappings);
    						}
    						Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
    						//将properties文件合并到Map格式的handlerMappings中
    						CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
    						this.handlerMappings = handlerMappings;
    					}
    					catch (IOException ex) {
    
    
    						throw new IllegalStateException(
    								"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
    					}
    				}
    			}
    		}
    		return this.handlerMappings;
    	}

4,标签解析

得到了解析器和分析的元素后,Spring就可以将解析工作委托给自定义解析器去解析了

让我们回到BeanDefinitionParserDelegate的handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));中,进入NamespaceHandlerSupport中的parse方法

    	/**
    	 * Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
    	 * registered for that {@link Element}.
    	 */
    	public BeanDefinition parse(Element element, ParserContext parserContext) {
    
    
    		//寻找解析器,并进行解析操作
    		return findParserForElement(element, parserContext).parse(element, parserContext);
    	}
     
    	/**
    	 * Locates the {@link BeanDefinitionParser} from the register implementations using
    	 * the local name of the supplied {@link Element}.
    	 */
    	private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    
    
    		//获取元素名称,也就是<myname:user 中的user,此时localname为user
    		String localName = parserContext.getDelegate().getLocalName(element);
    		//根据user找到对应的解析器也就是在
    		//registerBeanDefinitionParser("user", new UserBeanDefinitionParser());注册的解析器
    		BeanDefinitionParser parser = this.parsers.get(localName);
    		if (parser == null) {
    
    
    			parserContext.getReaderContext().fatal(
    					"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    		}
    		return parser;
    	}

让我们再跟踪到AbstractBeanDefinitionParser的parse()方法

    public final BeanDefinition parse(Element element, ParserContext parserContext) {
    
    
    		AbstractBeanDefinition definition = parseInternal(element, parserContext);
    		if (definition != null && !parserContext.isNested()) {
    
    
    			try {
    
    
    				String id = resolveId(element, definition, parserContext);
    				if (!StringUtils.hasText(id)) {
    
    
    					parserContext.getReaderContext().error(
    							"Id is required for element '" + parserContext.getDelegate().getLocalName(element)
    									+ "' when used as a top-level tag", element);
    				}
    				String[] aliases = new String[0];
    				String name = element.getAttribute(NAME_ATTRIBUTE);
    				if (StringUtils.hasLength(name)) {
    
    
    					aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
    				}
    				//将AbstractBeanDefinition转换成BeanDefinitionHolder
    				BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
    				registerBeanDefinition(holder, parserContext.getRegistry());
    				if (shouldFireEvents()) {
    
    
    					//需要通知监听器进行处理
    					BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
    					postProcessComponentDefinition(componentDefinition);
    					parserContext.registerComponent(componentDefinition);
    				}
    			}
    			catch (BeanDefinitionStoreException ex) {
    
    
    				parserContext.getReaderContext().error(ex.getMessage(), element);
    				return null;
    			}
    		}
    		return definition;
    	}

虽然说是对自定义配置文件的解析,但是我们可以看到,在这个函数中大部分的代码用来处理将解析后的AbstractBeanDefinition转换为BeanDefinitionHolder并注册的功能,而真正去做解析的事情委托了给parseInternal,真是这句代码调用了我们的自定义解析函数。在parseInternal中,并不是直接调用自定义的doParse函数,而是惊醒了一些列的数据准备,包括对beanClass,scope,lazyInit等属性的准备。

接下来,让我们看看parseInternal方法,先跟踪到AbstractSingleBeanDefinitionParser

    	/**
    	 * Creates a {@link BeanDefinitionBuilder} instance for the
    	 * {@link #getBeanClass bean Class} and passes it to the
    	 * {@link #doParse} strategy method.
    	 * @param element the element that is to be parsed into a single BeanDefinition
    	 * @param parserContext the object encapsulating the current state of the parsing process
    	 * @return the BeanDefinition resulting from the parsing of the supplied {@link Element}
    	 * @throws IllegalStateException if the bean {@link Class} returned from
    	 * {@link #getBeanClass(org.w3c.dom.Element)} is {@code null}
    	 * @see #doParse
    	 */
    	@Override
    	protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
    
    
    		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
    		String parentName = getParentName(element);
    		if (parentName != null) {
    
    
    			builder.getRawBeanDefinition().setParentName(parentName);
    		}
    		//获取自定义标签中的class,此时会调用自定义解析器,如UserBeanDefinitionParser中的getBeanClass方法
    		Class<?> beanClass = getBeanClass(element);
    		if (beanClass != null) {
    
    
    			builder.getRawBeanDefinition().setBeanClass(beanClass);
    		}
    		else {
    
    
    			//若子类没有重写getBeanClass方法则尝试检查子类是否重写getBeanClassName方法
    			String beanClassName = getBeanClassName(element);
    			if (beanClassName != null) {
    
    
    				builder.getRawBeanDefinition().setBeanClassName(beanClassName);
    			}
    		}
    		builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
    		if (parserContext.isNested()) {
    
    
    			// Inner bean definition must receive same scope as containing bean.
    			builder.setScope(parserContext.getContainingBeanDefinition().getScope());
    		}
    		if (parserContext.isDefaultLazyInit()) {
    
    
    			// Default-lazy-init applies to custom bean definitions as well.
    			builder.setLazyInit(true);
    		}
    		doParse(element, parserContext, builder);
    		return builder.getBeanDefinition();
    	}

十,注册BeanDefinition

让我们回到processBeanDefinition方法,找到beanDefinition的注册的方法BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());让我们来看看里面的详细实现:

    	/**
    	 * Register the given bean definition with the given bean factory.
    	 * @param definitionHolder the bean definition including name and aliases
    	 * @param registry the bean factory to register with
    	 * @throws BeanDefinitionStoreException if registration failed
    	 */
    	public static void registerBeanDefinition(
    			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    			throws BeanDefinitionStoreException {
    
     
     
    		// Register bean definition under primary name.
    		String beanName = definitionHolder.getBeanName();
    		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
     
    		// Register aliases for bean name, if any.
    		String[] aliases = definitionHolder.getAliases();
    		if (aliases != null) {
    
    
    			for (String aliase : aliases) {
    
    
    				registry.registerAlias(beanName, aliase);
    			}
    		}
    	}

通过上面可以看出,解完之后的beandefinition都会被注册到BeanDefinitionRegistry里面去,其中BeanDefinition的注册分两部分,一种是通过beanName注册,一种是通过别名aliase注册。

十一,通过beanName注册BeanDefinition

通过beanName注册BeanDefinition Spring究竟做了哪些事情呢,让我们来看看:

    	//---------------------------------------------------------------------
    	// Implementation of BeanDefinitionRegistry interface
    	//---------------------------------------------------------------------
     
    	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    			throws BeanDefinitionStoreException {
    
    
     
    		Assert.hasText(beanName, "Bean name must not be empty");
    		Assert.notNull(beanDefinition, "BeanDefinition must not be null");
     
    		if (beanDefinition instanceof AbstractBeanDefinition) {
    
    
    			try {
    
    
    				((AbstractBeanDefinition) beanDefinition).validate();
    			}
    			catch (BeanDefinitionValidationException ex) {
    
    
    				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    						"Validation of bean definition failed", ex);
    			}
    		}
     
    		BeanDefinition oldBeanDefinition;
     
    		synchronized (this.beanDefinitionMap) {
    
    
    			oldBeanDefinition = this.beanDefinitionMap.get(beanName);
    			if (oldBeanDefinition != null) {
    
    
    				if (!this.allowBeanDefinitionOverriding) {
    
    
    					throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    							"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
    							"': There is already [" + oldBeanDefinition + "] bound.");
    				}
    				else {
    
    
    					if (this.logger.isInfoEnabled()) {
    
    
    						this.logger.info("Overriding bean definition for bean '" + beanName +
    								"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
    					}
    				}
    			}
    			else {
    
    
    				this.beanDefinitionNames.add(beanName);
    				this.frozenBeanDefinitionNames = null;
    			}
    			this.beanDefinitionMap.put(beanName, beanDefinition);
    		}
     
    		if (oldBeanDefinition != null || containsSingleton(beanName)) {
    
    
    			resetBeanDefinition(beanName);
    		}
    	}
  1. 对AbstractBeanDefinition进行校验。在解析XML文件的时候我们进行过校验,之前是对XML格式的校验,现在做的校验是对AbstractBeanDefinition的MethodOverrides的校验
  2. 对beanName已经注册的情况做处理,如果设置了不允许覆盖bean,那么会直接抛出异常,否则是直接覆盖
  3. 加入map缓存
  4. 清楚解析之前留下的beanName的缓存。

十二,通过别名alias注册BeanDefinition

    public void registerAlias(String name, String alias) {
    
    
    		Assert.hasText(name, "'name' must not be empty");
    		Assert.hasText(alias, "'alias' must not be empty");
    		if (alias.equals(name)) {
    
    
    			this.aliasMap.remove(alias);
    		}
    		else {
    
    
    			if (!allowAliasOverriding()) {
    
    
    				String registeredName = this.aliasMap.get(alias);
    				if (registeredName != null && !registeredName.equals(name)) {
    
    
    					throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
    							name + "': It is already registered for name '" + registeredName + "'.");
    				}
    			}
    			checkForAliasCircle(name, alias);
    			this.aliasMap.put(alias, name);
    		}
    	}
  1. alias和beanName名字一样的情况处理,如果是名字一样,那么直接删除别名。
  2. alias覆盖处理。若aliasName已经指向了另一beanName则需要用户的设置进行处理。
  3. alias循环检查。当A->B存在时候,如果出现了A->C->B那么直接抛出异常。
  4. 注册alias

十三,通知监听器解析注册已完成

让我们回顾下processBeanDefinition方法,里面有说到,当bean注册完成之后便会调用getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));这里便提供了途径给开发人员对bean完成注册之后进行相应的拓展,目前Spring里面是空实现。

猜你喜欢

转载自blog.csdn.net/saienenen/article/details/112911660
今日推荐