上一篇梳理了XmlBeanDefinitionReader
解析注册BeanDefinition
的过程,但主要是针对默认标签的解析,这里主要介绍下其对于自定义标签的解析策略。
示例
扩展spring自定义配置标签大致需要以下几个步骤:
- 创建一个需要扩展的组件
- 定义一个XSD文件描述组件内容
- 创建一个文件,实现
BeanDefinitionParser
接口,用来解析XSD文件中的定义
- 创建一个Handler,扩展自
NamespaceHandlerSupport
,将组件注册到spring容器
- 编写spring.handlers和spring.schemas文件
User.java1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class User { private String name; private String email;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; } }
|
META-INF/user.xsd1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.shanhm1991.com/schema/user" xmlns="http://www.shanhm1991.com/schema/user" elementFormDefault="qualified">
<xs:element name="user"> <xs:complexType> <xs:attribute name="id" type="xs:string" /> <xs:attribute name="name" type="xs:string" /> <xs:attribute name="email" type="xs:string" /> </xs:complexType> </xs:element> </xs:schema>
|
UserBeanDefinitionParser.java1 2 3 4 5 6 7 8 9 10 11 12 13
| public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return User.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder builder) { builder.addPropertyValue("name", element.getAttribute("name")); builder.addPropertyValue("email", element.getAttribute("email")); } }
|
UserNamespaceHandler.java1 2 3 4 5 6 7
| public class UserNamespaceHandler extends NamespaceHandlerSupport{
@Override public void init() { registerBeanDefinitionParser("user", new UserBeanDefinitionParser()); } }
|
META-INF/spring.schemas1
| http\://www.shanhm1991.com/schema/user.xsd=META-INF/user.xsd
|
META-INF/spring.handlers1
| http\://www.shanhm1991.com/schema/user=test.test.UserNamespaceHandler
|
bean.xml1 2 3 4 5 6 7 8 9 10 11
| <?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:self="http://www.shanhm1991.com/schema/user" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.shanhm1991.com/schema/user http://www.shanhm1991.com/schema/user.xsd"> <self:user id="test" name="shanhm1991" email="shanhm1991@163.com" /> </beans>
|
App.java1 2 3 4 5 6 7 8
| public class App {
public static void main(String[] args) { BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("bean.xml")); User user = (User)beanFactory.getBean("test"); System.out.println(user.getName() + ":" + user.getEmail()); } }
|
BeanDefinitionParserDelegate
parseCustomElement
下面接着前文中的BeanDefinitionParserDelegate.parseCustomElement
来梳理一下其解析过程
getNamespaceHandlerResolver
首先是获取自定义标签处理器
org.springframework.beans.factory.xml.BeanDefinitionParserDelegate1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); } 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)); }
|
这里的NamespaceHandler
是由NamespaceHandlerResolver
根据namespaceUri
解析出来的,而NamespaceHandlerResolver
本身是在XmlBeanDefinitionReader
创建XmlReaderContext
时初始化和传入的
org.springframework.beans.factory.xml.XmlBeanDefinitionReader1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; } 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); }
|
resolveNamespaceHandler
NamespaceHandlerResolver
中也是依赖spring.handlers
中的配置来尝试获取NamespaceHandler
的,并在初始化之后立即调用其init()
方法,对应上面的示例,其实就是注册了namespace对应的BeanDefinitionParser
org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver1 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
| 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); } } }
|
findParserForElement
然后便是委托对应的NamespaceHandler
进行解析,不过它也是交给上面初始化时注册的BeanDefinitionParser
来进行解析的
org.springframework.beans.factory.xml.NamespaceHandlerSupport1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| private final Map<String, BeanDefinitionParser> parsers = new HashMap<>();
public BeanDefinition parse(Element element, ParserContext parserContext) { BeanDefinitionParser parser = findParserForElement(element, parserContext); return (parser != null ? parser.parse(element, parserContext) : null); } 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
自定义标签并不用从头开始解析,只需基于AbstractSingleBeanDefinitionParser
提供的模板方法重写其中的doParse
即可
org.springframework.beans.factory.xml.AbstractBeanDefinitionParser1 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
| 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; }
|
org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser1 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
| 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源码深度解析》 郝佳