继续笔记1中结尾处的自定义标签解析,delegate.parseCustomElement(ele)方法。
自定义标签:Spring提供了一个可扩展Schema的支持,扩展Spring自定义标签的步骤如下。
- 创建一个需要扩展的组件。
- 定义一个XSD文件描述组件内容。
- 创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义。
- 创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组件注册到Spring容器。
- 编写Spring.handlers和Spring.schemas。
1.第一步,创建一个普通的JavaBean。
public class User {
private String username;
private String email;
//get/set方法。
}
2.定义一个XSD文件描述组件。
<?xml version="1.0" encoding="utf-8" ?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.lexueba.com/schema/user"
xmlns:tns="http://www.lexueba.com/schema/user"
elementFormDefault="qualified">
<element name="user">
<complexType>
<attribute name="id" type="string"></attribute>
<attribute name="username" type="string"></attribute>
<attribute name="email" type="string"></attribute>
</complexType>
</element>
</schema>
3.创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义。
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
protected Class getBeanClass(Element element){
return User.class;
}
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);
}
}
}
4.创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组件注册到Spring容器。
package com.msmk.cloud.spring.demo01;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class MyNamespaceHandler extends NamespaceHandlerSupport{
@Override
public void init() {
registerBeanDefinitionParser("user",new UserBeanDefinitionParser());
}
}
5.编写Spring.handlers和Spring.schemas文件,默认位置在工程的/META-INF/文件夹下,当然,你可以通过Spring的扩展或者修改源码的方式改变路径。
//Spring.handlers
http://www.lexueba.com.schema/user=com.msmk.cloud.spring.demo01.MyNamespaceHandler
//Spring.schemas
http://www.lexueba.com.schema/user.xsd=META-INF/Spring-test.xsd
6.创建测试的配置文件,在配置文件中引入对应的命名空间和对应的XSD。
<?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:myname="http://www.lexueba.com/schema/user"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.lexueba.com/schema/user http://www.lexueba.com/schema/user.xsd" >
<myname:user id="testbean" username="aaa" email="bbb"/>
</beans>
7.测试。
public static void main(String[] args) {
ApplicationContext bf = new ClassPathXmlApplicationContext("test.xml");
User user = (User)bf.getBean("testBean");
System.out.println(user.getUsername() + "##" + user.getEmail());
}
继续笔记1中结尾处的自定义标签解析。通过delegate.parseCustomElement(ele)方法进行跟踪。
public BeanDefinition parseCustomElement(Element ele) {
return this.parseCustomElement(ele, (BeanDefinition)null);
}
继续跟踪:
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
//获取对应的命名空间。
String namespaceUri = this.getNamespaceURI(ele);
//根据命名空间找到对应的NamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if(handler == null) {
this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
} else {
//调用自定义的NamespaceHandler进行解析。
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
}
分别对每一个方法进行跟踪:
getNamespaceURI,主要用于区分默认标签和自定义标签都是以命名空间为基础,如何根据对应的命名空间去提取元素,spring是直接调用org.w3c.dom.Node中已经提供了的方法。
public String getNamespaceURI(Node node) { return node.getNamespaceURI(); }
resolve方法
//以下代码调用了resolve方法。 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); //继续跟踪resolve方法。 public NamespaceHandler resolve(String namespaceUri) { //获取所有已经配置的handler映射 Map handlerMappings = this.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 err = ClassUtils.forName(className, this.classLoader); if(!NamespaceHandler.class.isAssignableFrom(err)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } else { //初始化类 NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(err); //调用自定义的NamespaceHandlerder的初始化方法。 namespaceHandler.init(); //记录在缓存。 handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } } catch (ClassNotFoundException var7) { throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", var7); } catch (LinkageError var8) { throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", var8); } } }
解释上面resolve方法中的每一个方法:
this.getHandlerMappings()
private Map<String, Object> getHandlerMappings() { //如果没缓存则开始进行缓存。 if(this.handlerMappings == null) { synchronized(this) { if(this.handlerMappings == null) { try { //在构造函数中已经被初始化为:META-INF/Spring.handlers Properties ex = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if(this.logger.isDebugEnabled()) { this.logger.debug("Loaded NamespaceHandler mappings: " + ex); } ConcurrentHashMap handlerMappings = new ConcurrentHashMap(ex.size()); //将Properties格式文件合并到Map格式的handlerMappings中 CollectionUtils.mergePropertiesIntoMap(ex, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException var5) { throw new IllegalStateException("Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", var5); } } } } return this.handlerMappings; }
分析一下MyNamespaceHandler父类NamespaceHandlerSupport中的parse方法:
public BeanDefinition parse(Element element, ParserContext parserContext) {
return this.findParserForElement(element, parserContext).parse(element, parserContext);
}
//继续追踪findParserForElement方法:
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
//获取元素名称,也就是<myname:user中的user,此时localName为user。
String localName = parserContext.getDelegate().getLocalName(element);
//获取解析器。
BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName);
if(parser == null) {
parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}
//继续追踪parse方法:
public final BeanDefinition parse(Element element, ParserContext parserContext) {
//此处的parseInternal才是真正做事的方法。
AbstractBeanDefinition definition = this.parseInternal(element, parserContext);
if(definition != null && !parserContext.isNested()) {
try {
String ex = this.resolveId(element, definition, parserContext);
if(!StringUtils.hasText(ex)) {
parserContext.getReaderContext().error("Id is required for element \'" + parserContext.getDelegate().getLocalName(element) + "\' when used as a top-level tag", element);
}
String[] aliases = null;
if(this.shouldParseNameAsAliases()) {
String holder = element.getAttribute("name");
if(StringUtils.hasLength(holder)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(holder));
}
}
//将AbstractBeanDefinition转换为BeanDefinitionHolder并注册
BeanDefinitionHolder holder1 = new BeanDefinitionHolder(definition, ex, aliases);
this.registerBeanDefinition(holder1, parserContext.getRegistry());
if(this.shouldFireEvents()) {
//需要通知监听器进行处理。
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder1);
this.postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
} catch (BeanDefinitionStoreException var8) {
parserContext.getReaderContext().error(var8.getMessage(), element);
return null;
}
}
return definition;
}
上面追踪parse方法中的核心parseInternal方法源码如下。
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
String parentName = this.getParentName(element);
if(parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
//获取自定义标签中的class,此时会调用自定义解析器如UserBeanDefinitionParser中的getBeanClass方法。
Class beanClass = this.getBeanClass(element);
if(beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
} else {
//若子类没有重写getBeanClass方法则尝试检查子类是否重写getBeanClass方法。
String beanClassName = this.getBeanClassName(element);
if(beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
if(parserContext.isNested()) {
//若存在父类则使用父类的scope属性。
builder.setScope(parserContext.getContainingBeanDefinition().getScope());
}
if(parserContext.isDefaultLazyInit()) {
//配置懒加载。
builder.setLazyInit(true);
}
//调用子类重写的doParse方法进行解析。
this.doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
//继续跟踪doParse方法。
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
this.doParse(element, builder);
}