文章内容参考了《Spring源码深度解析》一书。自己照着书中内容做了一遍,不懂的地方以及采坑的地方会在文中记录。
推荐一篇post,关于Spring配置文件的命名空间:
https://www.cnblogs.com/gonjan-blog/p/6637106.html
我们暂时只是知道使用Spring的常规标签,加个bean,事务,Aop等等。随着满足业务的需求,同时降低程序员的工作量,我们有时需要自己定制一些标签。话补多少,下面进入主题。
自定义标签的使用
扩展Spring自定义标签大致需要如下几步:(把大象装冰箱,需要三步,开门,放,关门。。。)
1.创建需要扩展的组件
2.定义XSD文件描述组件内容
3.创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义
4.创建Handler文件,扩展字NamespaceHandlerSupport,目的是将组件注册到Spring容器
5.编写Spring.handlers和Spring.schemas文件
上述,五点看完了,我最开始时一头雾水,这是什么啊?别着急,跟哥往下走。下面有好东西,保证不打晕你。
例子:
1.创建POJO,接收配置文件
1 package test.customtag; 2 3 public class User { 4 private String userName; 5 public String getUserName() { 6 return userName; 7 } 8 public void setUserName(String userName) { 9 this.userName = userName; 10 } 11 private String email; 12 public String getEmail() { 13 return email; 14 } 15 public void setEmail(String email) { 16 this.email = email; 17 } 18 }
2.定义一个XSD文件描述组件内容
src/main/resources/META-INF/user.xsd
1 <?xml version="1.0" encoding="UTF-8"?> 2 <schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/schema/user" xmlns:tns="http://www.example.org/schema/user" elementFormDefault="qualified"> 3 <element name="user2"> 4 <complexType> 5 <attribute name ="id" type = "string"/> 6 <attribute name ="userName" type = "string"/> 7 <attribute name ="email" type = "string"/> 8 </complexType> 9 </element> 10 </schema>
描述了一个targetNamespace,并且创建了一个element user2。里面有三个attribute。主要是为了验证Spring配置文件中的自定义格式。再进一步解释,就是,Spring位置文件中使用的user2自定义标签中,属性只能是上面的三种,有其他的属性的话,就会报错。
网上有人说element ,complexType等等这些标签要用XSD开头的标签,但是我这里没有用也能成功的执行,我猜测可能是spring的版本问题。(我这个猜测没有依据)
大家对这个XSD可能会有疑问,不要担心。推荐一篇post,里面讲解的很好,地址放在文章开头那里。
3.创建文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义
1 package test.customtag; 2 3 4 import org.springframework.beans.factory.support.BeanDefinitionBuilder; 5 import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; 6 import org.springframework.util.StringUtils; 7 import org.w3c.dom.Element; 8 9 public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { 10 11 @SuppressWarnings("rawtypes") 12 protected Class getBeanClass(Element element) { 13 return User.class; 14 } 15 16 protected void doParse(Element element, BeanDefinitionBuilder bean) { 17 String userName = element.getAttribute("userName"); 18 String email = element.getAttribute("email"); 19 if (StringUtils.hasText(userName)) { 20 bean.addPropertyValue("userName", userName); 21 } 22 if (StringUtils.hasText(email)){ 23 bean.addPropertyValue("email", email); 24 } 25 26 } 27 }
上文,虽说是继承了AbstractSingleBeanDefinitionParser ,但根本上来说,是实现了BeanDefinitionParser接口。
4.创建Handler文件,扩展自NamespaceHandlerSupport,目的是将组件注册到Spring容器中。
1 package test.customtag; 2 3 import org.springframework.beans.factory.xml.NamespaceHandlerSupport; 4 5 public class MyNamespaceHandler extends NamespaceHandlerSupport { 6 7 public void init() { 8 9 registerBeanDefinitionParser("user2", new UserBeanDefinitionParser()); 10 } 11 12 }
5.编写Spring.handlers和Spring.schemas文件
路径:src/main/resources/META-INF/Spring.handlers
1 http\://www.example.org/schema/user=test.customtag.MyNamespaceHandler
路径:src/main/resources/META-INF/Spring.schemas
1 http\://www.example.org/schema/user.xsd=META-INF/user.xsd
\ 是转义字符的概念。
6.创建测试配置文件
路径:src/main/resources/test.xml
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:myname2="http://www.example.org/schema/user" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.example.org/schema/user http://www.example.org/schema/user.xsd"> 7 8 <myname2:user2 id = "testbean" userName = "lee" email = "bbb"/> 9 </beans>
在第4行这里引入了myname2对应的命名空间,它会到第六行(http://www.example.org/schema/user.xsd)找到对应XSD文件进行check。第六行的user.xsd文件的位置,到步骤5里的Spring.schemas里去参照。
在这里说明一下自己遇到的疑问:第八行,自己eclipse报了红叉,信息如下:
Multiple annotations found at this line:
- cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element ‘myname2:user2‘.
- schema_reference.4: Failed to read schema document ‘http://www.example.org/schema/user.xsd‘, because 1) could not find the
document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
但是,我没有解决它,直接执行第6步的代码,最后成功执行了,搞不懂原因。
6.测试。
1 package test.customtag; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 public class Tst { 7 public static void main(String[] args) { 8 ApplicationContext beans=new ClassPathXmlApplicationContext("classpath:test.xml"); 9 User user=(User)beans.getBean("testbean"); 10 System.out.println("username:"+user.getUserName()+" "+"email:"+user.getEmail()); 11 } 12 }
文章记录的不是特别的详细,有一些知识点没有全面的记录。但是,跟着一步步做,Spring自定义标签的实现,应该是没有问题了。稍后,有时间,会记录Sping源码中,关于自定义标签解析的部分。与君共勉。