Spring默认会将xml中全部 <bean> 等标签解析为BeanDefinition抽象对象,自定义标签的本质是扩展Spring解析的标签类型,使其从自定义标签转化为BeanDefinition的过程。java
1. 建立映射标签实体JavaBean: Person/Usernode
package com.zhiwei.basic.tag; import java.util.List; public class Person { private String personName; private Integer age; private List<String> nickName; public String getPersonName() { return personName; } public void setPersonName(String personName) { this.personName = personName; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public List<String> getNickName() { return nickName; } public void setNickName(List<String> nickName) { this.nickName = nickName; } @Override public String toString() { return "Person [name=" + personName + ", age=" + age + ", nickName=" + nickName + "]"; } }
package com.zhiwei.basic.tag; public class User { private String userName; private String email; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "User [userName=" + userName + ", email=" + email + "]"; } }
2. 建立标签解析器spring
package com.zhiwei.basic.tag; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.util.StringUtils; import org.w3c.dom.Element; /** * XML中的Bean标签解析器:将<dubbo:user>解析为User对象 */ public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<User> getBeanClass(Element element) { return User.class; } @Override 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); } } }
package com.zhiwei.basic.tag; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.util.StringUtils; import org.w3c.dom.Element; /** * XML中的Bean标签解析器:将<dubbo:user>解析为User对象 */ public class PersonBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { protected Class<Person> getBeanClass(Element element) { return Person.class; } protected void doParse(Element element, BeanDefinitionBuilder bean) { String userName = element.getAttribute("personName"); String email = element.getAttribute("age"); String nickName = element.getAttribute("nickName"); if (StringUtils.hasText(userName)) { bean.addPropertyValue("personName", userName); } if (StringUtils.hasText(email)) { bean.addPropertyValue("age", email); } if (StringUtils.hasText(nickName)) { bean.addPropertyValue("nickName", nickName); } } }
3.建立命名空间处理器:NamespaceHandler缓存
package com.zhiwei.basic.tag; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; /** * 命名空间处理器: */ public class NamespaceHandler extends NamespaceHandlerSupport { /** * DefaultBeanDefinitionDocumentReader调用 * 调用时间:自定义元素定义以后,自定义元素真正被解析以前 */ @Override public void init() { registerBeanDefinitionParser("user", new UserBeanDefinitionParser()); registerBeanDefinitionParser("person", new PersonBeanDefinitionParser()); } }
4.建立自定义标签的XML约束文件:XSDapp
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns="http://www.zhiwei.com/schema/user" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.zhiwei.com/schema/user" elementFormDefault="qualified" attributeFormDefault="unqualified"> <!-- 定义标签元素及其类型 --> <xsd:element name="user" type="user" /> <!-- 定义标签元素的类型 --> <xsd:complexType name="user"> <xsd:attribute name="id" type="xsd:string" /> <xsd:attribute name="userName" type="xsd:string" default="zhangsan"/> <xsd:attribute name="email" type="xsd:string" /> </xsd:complexType> <xsd:element name="person" type="person" /> <xsd:complexType name="person"> <xsd:attribute name="id" type="xsd:string" /> <xsd:attribute name="personName" type="xsd:string"/> <xsd:attribute name="age" type="xsd:integer"/> <!-- 属性引用时name、ref属性不能同时出现 --> <xsd:attribute name="nickName" type="nickName"/> </xsd:complexType> <xsd:simpleType name="nickName"> <xsd:list itemType="xsd:string"/> </xsd:simpleType> </xsd:schema>
5.在项目的根类路径配置自定义标签命名空间、解析器等信息: META-INF框架
注意:文件名这2个文件名不能自定义修改,Spring框架内部对文件名进行了硬编码指定。dom
spring.handlerside
http\://www.zhiwei.com/schema/user=com.zhiwei.basic.tag.NamespaceHandler
spring.schemaspost
http\://www.zhiwei.com/schema/user/user.xsd=com/zhiwei/basic/tag/user.xsd
6. 测试自定义标签测试
Spring配置文件:
<?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:dubbo="http://www.zhiwei.com/schema/user" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.zhiwei.com/schema/user http://www.zhiwei.com/schema/user/user.xsd"> <dubbo:user id="user" userName="cong" email="mail.163.com"/> <!-- 定义List类型的Bean --> <dubbo:person id="person" personName="zhangsan" age="10" nickName="zhangsan,lisi,wangwu"/> </beans>
测试类:
package com.zhiwei.basic.tag; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainTest { @SuppressWarnings("resource") public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("com/zhiwei/basic/tag/applicationContext.xml"); User user = (User) ac.getBean("user"); System.out.println("user--------" + user); Person person = (Person) ac.getBean("person"); System.out.println("person---------"+person); } }
效果:
项目层次图:
核心类: XmlBeanDefinitionReader
1. Spring Bean 注册方法
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; }
2. 解析Spring XML标签元素
核心类: DefaultBeanDefinitionDocumentReader
protected void doRegisterBeanDefinitions(Element root) { BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
分析:Spring将bean标签的元素委托给delegate对象处理,parseBeanDefinitions()方法将XML文件的解析转交给delegate处理
parseBeanDefinitions 方法核心代码:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
分析:
parseDefaultElement(ele, delegate):解析Spring自带的标签:例如import/alias/bean/beans
parseCustomElement : 解析自定义标签
Spring标签具体解析核心类:BeanDefinitionParserDelegate
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); 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 handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
经过XML命名空间(http://www.zhiwei.com/schema/user) 获取命名空间解析器:
获取流程:经过 DefaultNamespaceHandlerResolver 解析 META-INF/spring.handlers,获取XML命令空间对应的名称空间解析器:com.zhiwei.basic.tag.NamespaceHandler,本质经过类权限限定名经过反射机制获取自定义名称空间解析器实例
this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
源码:
@Override 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("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); } } }
代码分析:
NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(handlerClass); namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler);
instantiateClass:反射机制实例化NamespaceHandler实例,接着按照自定义标签名和自定义标签解析器信息缓存到父类 NamespaceHandlerSupport 的parsers集合中,同时将XML名称空间及处理器存放在DefaultNamespaceHandlerResolver 的handlerMappings中,最终返回NamespaceHandler。
BeanDefinitionParserDelegate.parseCustomElement()
代码分析
handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
功能:事先已获取NamespaceHandler,并进行相关的配置信息缓存空间,这行代码是最底层解析标签并注入到IOC容器的具体实现。
具体分析handler.parse方法:本质调到Namespacehandler父类:NamespaceHandlerSupport方法
源码:
return findParserForElement(element, parserContext).parse(element, parserContext);
findParserForElement(element, parserContext) :从缓存中获取自定义标签解析器
parse(element, parserContext): 解析自定义标签,并注入IOC容器
具体parse方法分析:
源码:自定义解析器父类AbstractBeanDefinitionParser.parse()方法:
源码:
@Override 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) { parserContext.getReaderContext().error(ex.getMessage(), element); return null; } } return definition; }
代码分析:
AbstractBeanDefinition definition = parseInternal(element, parserContext);
分析:生成自定标签对应的BeanDefinition抽象,主要是调用父类AbstractSingleBeanDefinitionParser的parseInternal方法,方法内部调用咱们自定义标签解析器的doParse(element, parserContext, builder);方法,实现自定义标签的处理,支持自定义标签的解析过程已经结束。
自定义标签BeanDefinition注入IOC容器:
具体代码:
registerBeanDefinition(holder, parserContext.getRegistry());
主要是经过parserContext内部的readerContext成员的getRegistry()方法获取IOC容器,而后进行实质的Bean注入。