Spring 自定义标签配置解析注册Bean源码分析(二)——解析注册的完成

2. 自定义标签解析

自定义标签的解析由BeanDefinitionParserDelegate委托类的parseCustomElement方法来完成。

// 源码位置:org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element)
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
	return parseCustomElement(ele, null);
}

// 源码位置:org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
	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));
}

2.1. 获取NamespaceHandler

NamespaceHandler是解析自定义命名空间的统一接口,各命名空间通过实现该接口来定义解析流程。因此需要根据命名空间找到对应的NamespaceHandler来解析。

Spring XML默认标签配置解析注册Bean源码分析(四)——BeanDefinitionDocumentReader这篇文章里面,在创建XmlReaderContext的时候我们提到过一个参数getNamespaceHandlerResolver(),它创建了一个new DefaultNamespaceHandlerResolver(cl)对象放到了XmlReaderContext里面。从DefaultNamespaceHandlerResolver类名我们知道这是一个解析器,目的就是解析出我们所需要的NamespaceHandler。那么重点看一下这一行代码:

NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);

既然我们在创建ReaderContext的时候传入了DefaultNamespaceHandlerResolver,那么先通过this.readerContext.getNamespaceHandlerResolver()拿到DefaultNamespaceHandlerResolver,然后在调用DefaultNamespaceHandlerResolver的resolve方法得到对应的NamespaceHandler类。

// 源码位置:org.springframework.beans.factory.xml.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
 */
@Override
@Nullable
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 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();得到命名空间与NamespaceHandler的对应关系,我们看一下getHandlerMappings()方法。

// 源码位置:org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#getHandlerMappings
/**
 * Load the specified NamespaceHandler mappings lazily.
 */
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;
}

很显然其对应关系是从Properties文件中读取的:

Map<String, Object> handlerMappings = this.handlerMappings;
Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
handlerMappings = new ConcurrentHashMap<>(mappings.size());
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);

看一下handlerMappingsLocation参数,跟踪一下会发现,它的默认值是public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";。因此,classpath路径下所有的META-INF/spring.handlers文件都会被加载。我们看一下我们自己定义的spring.handlers,其内容为:http\://www.yanglh.com/schema/user=com.yanglh.ioc.custom.MyNamespaceHandler。我们回到resolve方法,Object handlerOrClassName = handlerMappings.get(namespaceUri);得到了String类型的类名com.yanglh.ioc.custom.MyNamespaceHandler,但resolve返回值是NamespaceHandler,所以还需要对com.yanglh.ioc.custom.MyNamespaceHandler进行实例化,如下:

// className = com.yanglh.ioc.custom.MyNamespaceHandler
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);

// 实例化com.yanglh.ioc.custom.MyNamespaceHandler,它实现了NamespaceHandler接口
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);

// 对com.yanglh.ioc.custom.MyNamespaceHandler对象初始化
namespaceHandler.init();

// 替换映射关系的Map,将String类型的NamespaceHandler类名,改为实际的NamespaceHandler对象
handlerMappings.put(namespaceUri, namespaceHandler);

return namespaceHandler;

对于namespaceHandler.init();,我们还记得当时定义MyNamespaceHandler类时的init方法的内容吗?看一下:

public void init() {
    registerBeanDefinitionParser("user",new UserBenDefinitionParser());
}

它注入了当自定标签为<自定义标签名称:user时,其对应的解析类为UserBenDefinitionParser。registerBeanDefinitionParser方法会把标签和BenDefinitionParser类注册到一个Map里。

protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
	this.parsers.put(elementName, parser);
}

我们可以认为,一个命名空间对应一个NamespaceHandler,一个NamespaceHandler里面可以有多个BeanDefinitionParser。
比如:<自定义标签名称:user对应UserBenDefinitionParser解析器;<自定义标签名称:car时我们再定义CarBenDefinitionParser解析器;我们看一下Spring自带的MvcNamespaceHandler,它是SpringMVC命名空间的NamespaceHandler,里面注入了<mvc:annotation-driven、<mvc:interceptors等一系列的BeanDefinitionParser解析器。

到这里,就拿到了NamespaceHandler对象。

2.2. 获取BeanDefinitionParser

handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

NamespaceHandler的parse方法用于解析并最终注册BeanDefinition。

ParserContext


BeanDefinition解析过程中传递相关配置及状态的上下文,里面保存了XmlReaderContext和BeanDefinitionParserDelegate对象。

由于我们自定义的MyNamespaceHandler并不是直接实现NamespaceHandler接口,而是通过继承NamespaceHandlerSupport类。所以handler.parse方法由父类NamespaceHandlerSupport来执行。

// 源码位置:org.springframework.beans.factory.xml.NamespaceHandlerSupport#parse
/**
 * Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
 * registered for that {@link Element}.
 */
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
	BeanDefinitionParser parser = findParserForElement(element, parserContext);
	return (parser != null ? parser.parse(element, parserContext) : null);
}

获取BeanDefinitionParser

// 源码位置:org.springframework.beans.factory.xml.NamespaceHandlerSupport#findParserForElement
/**
 * 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) {
	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;
}

localName是获取的XML节点的本地名称,节点的本地名称是节点限定名的一部分,出现在冒号之后. 限定名通常当作特定XML文档命名空间的一部分。例如在限定名<myname:user中user是本地名,myname是前缀。

通过本地名称,可以从parsers这个Map中拿到对应的BeanDefinitionParser对象(还记得MyNamespaceHandler的init方法吗?)。

2.3. BeanDefinitionParser解析

在上一节我们获取到了节点本地名对应的BeanDefinitionParser对象,下面将通过该对象的parse(element, parserContext)方法完成解析工作。在这之前我们先看一下我们自定义的BeanDefinitionParser其继承关系,如下图:

parse(element, parserContext)方法调用的是AbstractBeanDefinitionParser类。

// 源码位置:org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parse
@Override
@Nullable
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 = null;
			if (shouldParseNameAsAliases()) {
				String name = element.getAttribute(NAME_ATTRIBUTE);
				if (StringUtils.hasLength(name)) {
					aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
				}
			}
			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) {
			String msg = ex.getMessage();
			parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
			return null;
		}
	}
	return definition;
}

2.3.1. 创建生成AbstractBeanDefinition对象

AbstractBeanDefinition definition = parseInternal(element, parserContext);
// 源码位置:org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#parseInternal
/**
 * 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<?> beanClass = getBeanClass(element);
	if (beanClass != null) {
		builder.getRawBeanDefinition().setBeanClass(beanClass);
	}
	else {
		String beanClassName = getBeanClassName(element);
		if (beanClassName != null) {
			builder.getRawBeanDefinition().setBeanClassName(beanClassName);
		}
	}
	builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
	BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
	if (containingBd != null) {
		// Inner bean definition must receive same scope as containing bean.
		builder.setScope(containingBd.getScope());
	}
	if (parserContext.isDefaultLazyInit()) {
		// Default-lazy-init applies to custom bean definitions as well.
		builder.setLazyInit(true);
	}
	doParse(element, parserContext, builder);
	return builder.getBeanDefinition();
}

AbstractBeanDefinition对象由BeanDefinitionBuilder中获取得到,因此要想得到AbstractBeanDefinition首先需要创建BeanDefinitionBuilder对象,它是一个BeanDefinition的构建器,用于在实现Spring自定义命名空间处理程序时使用。它在创建的时候通过new GenericBeanDefinition()创建了一个GenericBeanDefinition类。

然后doParse(element, parserContext, builder);是我们自定义解析方式的程序。

// 源码位置:com.yanglh.ioc.custom.UserBenDefinitionParser#doParse
protected void doParse(Element element, BeanDefinitionBuilder bean) {
    String userName = element.getAttribute("userName");
    String email = element.getAttribute("email");

    if(StringUtils.hasText(userName)){
        bean.addPropertyValue("userName",userName);
    }
    if(StringUtils.hasText(email)){
        bean.addPropertyValue("email",email);
    }
}

上面就是我们自定义的解析程序,将属性和值赋值到BeanDefinitionBuilder类中BeanDefinition对象中。

做完以上步骤,我们也就得到AbstractBeanDefinition对象。

2.3.2. 注册BeanDefinition

BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
registerBeanDefinition(holder, parserContext.getRegistry());

到了这一步就和默认标签配置的流程一样了。先创建BeanDefinitionHolder对象持有BeanDefinition及相关信息,然后调用registerBeanDefinition方法。

// 源码位置:org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#registerBeanDefinition
/**
 * Register the supplied {@link BeanDefinitionHolder bean} with the supplied
 * {@link BeanDefinitionRegistry registry}.
 * <p>Subclasses can override this method to control whether or not the supplied
 * {@link BeanDefinitionHolder bean} is actually even registered, or to
 * register even more beans.
 * <p>The default implementation registers the supplied {@link BeanDefinitionHolder bean}
 * with the supplied {@link BeanDefinitionRegistry registry} only if the {@code isNested}
 * parameter is {@code false}, because one typically does not want inner beans
 * to be registered as top level beans.
 * @param definition the bean definition to be registered
 * @param registry the registry that the bean is to be registered with
 * @see BeanDefinitionReaderUtils#registerBeanDefinition(BeanDefinitionHolder, BeanDefinitionRegistry)
 */
protected void registerBeanDefinition(BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
	BeanDefinitionReaderUtils.registerBeanDefinition(definition, registry);
}

默认标签的BeanDefinition注册流程同样是由BeanDefinitionReaderUtils完成的,后面的流程请参考Spring XML默认标签配置解析注册Bean源码分析(六)——解析注册的完成这一篇的描述。

以上就是自定义标签的注册解析流程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值