Spring 工作流程是先加载解析xml配置文件:配置文件中存在默认的标签,也可以自定义标签。解析默认标签调用:
1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 2 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { 3 importBeanDefinitionResource(ele); 4 } 5 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { 6 processAliasRegistration(ele); 7 } 8 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { 9 processBeanDefinition(ele, delegate); 10 } 11 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { 12 // recurse 13 doRegisterBeanDefinitions(ele); 14 } 15 }
解析自定义标签调用:
1 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { 2 String namespaceUri = getNamespaceURI(ele); 3 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); 4 if (handler == null) { 5 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); 6 return null; 7 } 8 return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); 9 }
下面我们正式开始搞Spring自定义标签:
1.搞一个Bean: 这是一个很普通的pojo, 只是用来接收配置文件。
1 /** 2 * @filename: User.java 3 * @author: Wang Chinda 4 * @date: 2018-05-21 5 * @version: v1.0 6 * @modify_history: - 7 * 20180521 Wang Chinda create 8 * 20180521 Wang Chinda modify method() 9 */ 10 package com.itdoc.learn.source.custom; 11 12 /** 13 * @desc 自定义标签测试类 14 * @author Wang Chinda 15 * @create 2018-05-21 14:03 16 */ 17 public class User { 18 private String id; 19 private String userName; 20 private String email; 21 22 public String getId() { 23 return id; 24 } 25 26 public void setId(String id) { 27 this.id = id; 28 } 29 30 public String getUserName() { 31 return userName; 32 } 33 34 public void setUserName(String userName) { 35 this.userName = userName; 36 } 37 38 public String getEmail() { 39 return email; 40 } 41 42 public void setEmail(String email) { 43 this.email = email; 44 } 45 }
2.定义一个文件描述组件[xml schama difinition (xsd)]: 在classpath下创建一个META-INF文件夹, 在此文件夹下创建一个spring-user.xsd文件。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 3 xmlns="http://www.itdoc.com/schema/user" 4 targetNamespace="http://www.itdoc.com/schema/user" 5 elementFormDefault="qualified"> 6 <xsd:element name="user"> 7 <xsd:complexType> 8 <xsd:attribute name="id" type="xsd:string" /> 9 <xsd:attribute name="userName" type="xsd:string" /> 10 <xsd:attribute name="email" type="xsd:string" /> 11 </xsd:complexType> 12 </xsd:element> 13 </xsd:schema>
targetNamespace="http://www.itdoc.com/schema/user" 和xmlns="http://www.itdoc.com/schema/user"都是自定义的。
文件中的片段意义:
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
显示 schema 中用到的元素和数据类型来自命名空间 "http://www.w3.org/2001/XMLSchema"。同时它还规定了来自命名空间 "http://www.w3.org/2001/XMLSchema" 的元素和数据类型应该使用前缀 xsd
xmlns="http://www.itdoc.com/schema/user"
指出默认的命名空间是 "http://www.itdoc.com/schema/user"
targetNamespace="http://www.itdoc.com/schema/user"
显示被此 schema 定义的元素 (note, to, from, heading, body) 来自命名空间: "http://www.itdoc.com/schema/user"
elementFormDefault="qualified"
指出任何 XML 实例文档所使用的且在此 schema 中声明过的元素必须被命名空间限定
在上面的xsd文件描述中添加一个targetNamespace, 并在这个命名空间中定义了一个name为user的element, user有3个属性id、userName、email, 三个属性均为string类型。
3.创建一个用来解析xsd文件的定义和组件定义的文件并实现BeanDefinitionParser接口。此例中我们继承AbstractSingleBeanDefinitionParser抽象类.
1 /** 2 * @filename: UserBeanDefinitionParse.java 3 * @author: Wang Chinda 4 * @date: 2018-05-21 5 * @version: v1.0 7 * @modify_history: - 8 * 20180521 Wang Chinda create 9 * 20180521 Wang Chinda modify method() 10 */ 11 package com.itdoc.learn.source.custom; 12 13 import org.springframework.beans.factory.support.BeanDefinitionBuilder; 14 import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; 15 import org.springframework.util.StringUtils; 16 import org.w3c.dom.Element; 17 18 /** 19 * @desc 自定义bean定义解析 20 * @author Wang Chinda 21 * @create 2018-05-21 14:20 22 */ 23 public class UserBeanDefinitionParse extends AbstractSingleBeanDefinitionParser { 24 25 /** 26 * Element对应的类 27 * @param element 28 * @return 29 */ 30 @Override 31 protected Class<?> getBeanClass(Element element) { 32 return User.class; 33 } 34 35 @Override 36 protected void doParse(Element element, BeanDefinitionBuilder builder) { 37 String userName = element.getAttribute("userName"); 38 String email = element.getAttribute("email"); 39 40 // 将提取到的数据放入到BeanDefinitionBuilder中, 待完成所有bean的解析后统一注册到beanFactory中 41 if (StringUtils.hasText(userName)) { 42 builder.addPropertyValue("userName", userName); 43 } 44 45 if (StringUtils.hasText(email)) { 46 builder.addPropertyValue("email", email); 47 } 48 } 49 }
4.创建一个Handler文件, 扩展自NamespaceHandlerSupport, 目的是将组件注册到Spring容器中
/** * @filename: MyNameSpaceHandler.java * @author: Wang Chinda * @date: 2018-05-21 * @version: v1.0 * @modify_history: - * 20180521 Wang Chinda create * 20180521 Wang Chinda modify method() */ package com.itdoc.learn.source.custom; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; /** * @desc 自定义命名空间控制器 * @author Wang Chinda * @create 2018-05-21 14:28 */ public class MyNameSpaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("user", new UserBeanDefinitionParse()); } }
以上代码功能是当遇到<xxx:user 这样开头的元素, 就会交给UserBeanDefinitionParse去解析。
5.编写spring.handlers和spring.schemas文件, 默认位置是在classpath:/META-INF/文件夹下
注意:
1) 这两个文件名不允许改,若想更改的话,得需要连带更改spring源码文件。
2) 为什么将spring.handlers和spring.schemas放在META-INF文件夹下, 因为Spring默认到这个文件夹中寻找spring.handlers和spring.schemas文件。
spring.handlers:
http://www.itdoc.com/schema/user=com.itdoc.learn.source.custom.MyNameSpaceHandler
spring.schemas:
http://www.itdoc.com/schema/user.xsd=META-INF/spring-user.xsd
注意:
1) 冒号用来转译的。
2) 命名空间一定不要写错, 要和targetNamespace中定义的相同。
Spring加载自定义标签的大致流程是遇到自定义标签就会去spring.handler和spring.schemas中去找对应的handler和xsd,进而找到对应的MyNameSpaceHandler以及解析元素的UserBeanDefinitionParse, 从而完成了整个自定义标签的解析。
6.创建测试配置文件, 在文件中引入对应的命名空间及xsd, 便可以使用自定义标签。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:myTag="http://www.itdoc.com/schema/user" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.itdoc.com/schema/user http://www.itdoc.com/schema/user.xsd"> 7 8 <myTag:user id="customBeanTest" userName="name" email="itdoc@163.com"/> 9 10 </beans>
7.测试
1 /** 2 * @filename: CustomMain.java 3 * @author: Wang Chinda 4 * @date: 2018-05-21 5 * @version: v1.0 6 * @modify_history: - 7 * 20180521 Wang Chinda create 8 * 20180521 Wang Chinda modify method() 9 */ 10 package com.itdoc.learn.source.custom; 11 12 import org.springframework.context.ApplicationContext; 13 import org.springframework.context.support.ClassPathXmlApplicationContext; 14 15 /** 16 * @desc 测试 17 * @author Wang Chinda 18 * @create 2018-05-21 14:58 19 */ 20 public class CustomMain { 21 22 public static void main(String[] args) { 23 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("test/customTest.xml"); 24 User user = (User) applicationContext.getBean("customBeanTest"); 25 System.out.println(user.getEmail() + " ---- " + user.getUserName()); 26 } 27 }
控制台显示:
GitHub地址: https://github.com/wcd19901010/spring-01