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注入。