在xx培训机构的一次公开课上看到了spring自定义标签,手开始不自觉的痒痒起了。
废话少说, now , show code
一、自定义标签所返回的目标类, MyLabel, 此类没有什么好说的,就是一个平台的pojo
package cn.xmc168.label; public class MyLabel { private int userId; private String userName; private int age; public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public String getUserName() { return userName;
二、自定义解析器 BeanDefinitionParser, 此类的作用是将配置文件的自定义标签元素解析成BeanDefinition. 名称为 MyLabelBeanDefinitionParser , 实现 AbstractSingleBeanDefinitionParser ,并覆盖 getBeanClass(Element element) 和 doParse(Element element, BeanDefinitionBuilder builder) 两个方法。
package cn.xmc168.label.parsers; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.util.StringUtils; import org.w3c.dom.Element; import cn.xmc168.label.MyLabel; public class MyLabelBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return MyLabel.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder builder) { int age=0; String userName=null; try { userName = element.getAttribute("userName"); age = Integer.parseInt( element.getAttribute("age")); }catch(Exception e) { e.printStackTrace(); } if(age>0) { builder.addPropertyValue("age", age); } if(StringUtils.hasText(userName)) { builder.addPropertyValue("userName", userName); } } }
三、定义NamespaceHandler ,作用是将解析器添加到map中。
package cn.xmc168.label.handler; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import cn.xmc168.label.parsers.MyLabelBeanDefinitionParser; public class MyLabelNameSpaceHandler extends NamespaceHandlerSupport{ @Override public void init() { registerBeanDefinitionParser("mylabel", new MyLabelBeanDefinitionParser()); } }
四、定义自定义标签的约束文件, 存放位置是classpath:META-INF
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.xmc168.cn/schema/mylabel" targetNamespace="http://www.xmc168.cn/schema/mylabel" elementFormDefault="qualified"> <xsd:element name="mylabel" id="myLabel"> <xsd:complexType> <xsd:attribute name="id" type="xsd:string" /> <xsd:attribute name="userId" type="xsd:string" /> <xsd:attribute name="userName" type="xsd:string" /> <xsd:attribute name="age" type="xsd:string" /> </xsd:complexType> </xsd:element> </xsd:schema>
xmlns 和 targetNamespace 可以自己指定
五、创建spring.schemas文件, 存放位置是classpath:META-INF, 注意冒号需要转义。
http\://www.xmc168.cn/schema/mylabel.xsd=META-INF/spring-mylabel.xsd
六、创建spring.handlers ,存放位置是classpath:META-INF , 这个文件的作用是根据自定义标签的URI指定NameSpaceHandler
http\://www.xmc168.cn/schema/mylabel=cn.xmc168.label.handler.MyLabelNameSpaceHandler
已上是自定义标签的6个文件,现在看看在spring项目中怎么使用, 这里我们定义标签为myTag
七、applicationContext.xml 文件内容如下:
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:myTag="http://www.xmc168.cn/schema/mylabel" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.xmc168.cn/schema/mylabel http://www.xmc168.cn/schema/mylabel.xsd"> <myTag:mylabel id="myLabel" userId="11" userName="name" age="20" /> </beans>
package cn.xmc168.label; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class AppTest { @Autowired private MyLabel myLabel; @Test public void test1() { System.out.println(myLabel); } }
以上代码是测试通过的, 在测试的过程中也发现了一些坑
1、NameSpaceHandler.init方法注册的名字写错, 导致获取BeanDefinitionParser为空。后来是跟代码发现的这个问题。
2、AbstractBeanDefinitionParser.parse(Element element, ParserContext parserContext) 方法在获取id(估计是bean的id)时报错, 因为之前xsd中没有指定id, applicationContext.xml元素属性也没有指定id, 后来加上了id, 暂时解决这个问题, 但是我这里有一点疑惑的地方是:如果我的MyLabel类中也恰好有一个字段是int类型的名字也叫做id, 这里怎么配 ? 跟代码时,发现有这样一段代码
if (shouldGenerateId()) { return parserContext.getReaderContext().generateBeanName(definition); }
shouldGenerateId() 默认是false, 估计那里可以修改这个值, 修改成TRUE后可以不配bean的ID, 那么这个ID就可以用着类的ID了