解析bean标签子元素二
自定义标签的使用
自定义标签的使用大致分为以下步骤:
- 定义一个自定义标签组件,就是一个普通的
Java Bean
。
- 定义一个
xsd
文件,用来描述自定义标签的属性内容。
- 创建一个实现了
org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
接口的Parser
,用来解析xsd
文件中的自定义标签内容。
- 创建一个继承了
org.springframework.beans.factory.xml.NamespaceHandlerSupport
抽象类的NameSpaceHandler
,用于将自定义标签组件注册到 Spring 容器。
- 编写
spring.handlers
和 spring.schemas
文件,放在resource/META-INF
目录下。
文件结构图:
自定义标签组件
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 39 40 41 42 43 44 45
| public class Hello { private String id; private Integer age; private String name; private String address;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; } public Hello() { }
public void sayHello() { System.out.println(this.name + "今年" + this.age + "岁," + "住在" + this.address); } }
|
定义xsd
文件
该文件用来对自定义标签组件进行描述,还定义了xmlns="http://www.jievhaha.com/schema/hello"
和targetNamespace="http://www.jievhaha.com/schema/hello"
1 2 3 4 5 6 7 8 9 10 11
| <?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.jievhaha.com/schema/hello" targetNamespace="http://www.jievhaha.com/schema/hello" elementFormDefault="qualified"> <xsd:element name="hello"> <xsd:complexType> <xsd:attribute name="id" type="xsd:string"/> <xsd:attribute name="age" type="xsd:string" /> <xsd:attribute name="name" type="xsd:string" /> <xsd:attribute name="address" type="xsd:string" /> </xsd:complexType> </xsd:element> </xsd:schema>
|
定义Parser
类
该类继承了AbstractSingleBeanDefinitionParser
。
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
| public class HelloDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return Hello.class; }
@Override protected void doParse(Element element, BeanDefinitionBuilder builder) { String id = element.getAttribute("id"); String age = element.getAttribute("age"); String name = element.getAttribute("name"); String address = element.getAttribute("address"); if(StringUtils.hasLength("id")){ builder.addPropertyValue("id", id); } if(StringUtils.hasLength(age)){ builder.addPropertyValue("age", age); } if(StringUtils.hasText(name)){ builder.addPropertyValue("name", name); } if(StringUtils.hasLength(address)){ builder.addPropertyValue("address", address); } } }
|
定义NameSpaceHandler
类
该类继承了NamespaceHandlerSupport
。
1 2 3 4 5 6
| public class HelloNameSpaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("hello",new HelloDefinitionParser()); } }
|
定义spring.handlers
和spring.schemas
文件
1 2 3 4 5
| #spring.handlers http\://www.jievhaha.com/schema/hello=com.jievhaha.HelloNameSpaceHandler
#spring.schemas http\://www.jievhaha.com/schema/hello.xsd=hello.xsd
|
配置spring配置文件并测试
spring.xml
1 2 3 4 5 6 7 8 9
| <?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:mytag="http://www.jievhaha.com/schema/hello" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.jievhaha.com/schema/hello http://www.jievhaha.com/schema/hello.xsd">
<mytag:hello id="hello" name="jievhaha" age="27" address="山西"/> </beans>
|
MyApplication.java
1 2 3 4 5 6 7 8
| public class MyApplication { public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("hello.xml"); Hello hello = (Hello) applicationContext.getBean("hello"); hello.sayHello(); } }
|
解析自定义标签
parseCustomElement()
方法
applicationContext.xml文件开始读取的入口方法:DefaultBeanDefinitionDocumentReader.parseBeanDefinitions()
方法,该方法负责标签的解析工作,根据命名空间的不同进行不同标签的解析。其中,自定义标签由BeanDefinitionParserDelegate
的 parseCustomElement(Element ele, BeanDefinition containingBd)
方法来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Nullable public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); }
@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)); }
|
getNamespaceURI()
方法
先获取了NamespaceURI:
1 2 3 4
| @Nullable public String getNamespaceURI(Node node) { return node.getNamespaceURI(); }
|
getNamespaceHandlerResolver()
方法
调用 XmlReaderContext.getNamespaceHandlerResolver()
方法,获得命名空间的解析器。
1 2 3 4
| private final NamespaceHandlerResolver namespaceHandlerResolver; public final NamespaceHandlerResolver getNamespaceHandlerResolver() { return this.namespaceHandlerResolver; }
|
NamespaceHandlerResolver
的初始化:
在《注册BeanDefinitions》中提到注册BeanDefinitions时:
- 首先,是通过
XmlBeanDefinitionReader
的createBeanDefinitionDocumentReader()
方法,获取 Document解析器BeanDefinitionDocumentReader
实例。
- 然后,调用
BeanDefinitionDocumentReader
实例的registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
方法,进行注册。而该方法需要提供两个参数,一个是Document
实例 doc
,一个是XmlReaderContext
实例 readerContext
。
readerContext
实例对象由XmlBeanDefinitionReader
的createReaderContext(Resource resource)
方法创建。namespaceHandlerResolver
实例对象就是在这个时候初始化的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public XmlReaderContext createReaderContext(Resource resource) { return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, getNamespaceHandlerResolver()); }
public NamespaceHandlerResolver getNamespaceHandlerResolver() { if (this.namespaceHandlerResolver == null) { this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver(); } return this.namespaceHandlerResolver; }
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() { ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader()); return new DefaultNamespaceHandlerResolver(cl); }
|
resolve()
方法
由上可以看出this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri)
最终调用的就是DefaultNamespaceHandlerResolver
类的resolve()
方法。
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 39 40 41
| @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); } } }
|
获取所有已经配置的 Handler 映射,通过getHandlerMappings()
方法
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 39 40 41 42 43 44 45 46 47 48 49
|
private final String handlerMappingsLocation;
@Nullable private volatile Map<String, Object> handlerMappings;
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; }
|
执行完getHandlerMappings()
获取了Handler
映射后,之后还会执行NamespaceHandler.init()
方法,该方法主要是将自定义标签解析器进行注册,调用的就是我们上边实例中的HelloNameSpaceHandler.init()
方法,内部其实还是调用了父类NamespaceHandlerSupport
的registerBeanDefinitionParser()
来注册指定元素的BeanDefinitionParser
解析器,其实就是将映射关系放在一个 Map 结构的 parsers 对象中。。
1 2 3 4 5 6
| public class HelloNameSpaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("hello",new HelloDefinitionParser()); } }
|
NamespaceHandlerSupport
的registerBeanDefinitionParser()
:
1 2 3 4 5 6 7 8
| private final Map<String, BeanDefinitionDecorator> decorators = new HashMap<>();
protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) { this.decorators.put(elementName, dec); }
|
parse()
方法
执行完this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
获取了相应的Handler
后,然后调用parse()
方法执行自定义标签的解析。返回的NamespaceHandlerHandler
是我们自己定义的HelloNameSpaceHandler
,我们定义的继承了NamespaceHandlerSupport
,故其实调用的是NamespaceHandlerSupport.parse()
:
1 2 3 4 5 6
| @Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { BeanDefinitionParser parser = findParserForElement(element, parserContext); return (parser != null ? parser.parse(element, parserContext) : null); }
|
- 调用
findParserForElement(Element element, ParserContext parserContext)
方法,获取对应的 BeanDefinitionParser
实例。实际上,其实就是获取在NamespaceHandlerSupport
的 registerBeanDefinitionParser()
方法里面注册的实例对象,注册是在我们执行init()
的时候。
1 2 3 4 5 6 7 8 9 10 11 12 13
| @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; }
|
- 最后执行
parse()
,得到的BeanDefinitionParser
其实就是我们自己定义的HelloDefinitionParser
继承了AbstractSingleBeanDefinitionParser
,而AbstractSingleBeanDefinitionParser
又继承了AbstractBeanDefinitionParser
,所以最终执行的是该类的parse()
方法。
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 39 40 41 42
| @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; }
|
核心在 parseInternal(Element element, ParserContext parserContext)
方法。为什么这么说?因为该方法返回的是AbstractBeanDefinition
对象。从前面默认标签的解析过程来看,我们就可以判断该方法就是将标签解析为AbstractBeanDefinition
,且后续代码都是将 AbstractBeanDefinition
转换为 BeanDefinitionHolder
对象。所以真正的解析工作都交由parseInternal(Element element, ParserContext parserContext)
方法来实现。
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 39 40
|
@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) { builder.setScope(containingBd.getScope()); } if (parserContext.isDefaultLazyInit()) { builder.setLazyInit(true); } doParse(element, parserContext, builder); return builder.getBeanDefinition(); }
|
总结
自定义标签解析的大致流程:
- 首先,会加载
spring.handlers
文件,将其中内容进行一个解析,形成 <namespaceUri, 类路径>
这样的一个映射。
- 然后,根据获取的
namespaceUri
就可以得到相应的类路径,对其进行初始化等到相应的 NamespaceHandler 对象。
- 之后,调用该 NamespaceHandler 的
parse(...)
方法,在该方法中根据标签的 localName
得到相应的 BeanDefinitionParser 实例对象。
- 最后,调用该 BeanDefinitionParser 的
parse(...)
方法。该方法定义在 AbstractBeanDefinitionParser 抽象类中,核心逻辑封装在其 parseInternal(...)
方法中,该方法返回一个 AbstractBeanDefinition 实例对象,其主要是在 AbstractSingleBeanDefinitionParser 中实现。对于自定义的 Parser 类,其需要实现 getBeanClass()
或者 getBeanClassName()
任一方法,和 doParse(...)
方法。
流程图: