Spring 实现自定义标签解析
解析自定义标签源码:
DefaultBeanDefinitionDocumentReader#parseBeanDefinitions->delegate.parseCustomElement(ele)
跟踪源码可以看到,Spring 解析自定义标签时,会从 META-INF/spring.handlers 读取 handler 配置,不同的 namespace 有不同的 handler 处理方法。
handler 是一个 NamespaceHandlerSupport 的子类,需要重写 init() 并在其中注册 registerBeanDefinitionParser(name, parser)。
parser 是一个实现了 BeanDefinitionParser 接口的类,可以重写 doParse(element,builder) 其中 element 是一个 xml dom 对象,builder 是一个建造者模式对象 可以用来注册对象属性等。
其实到这里自定义标签的解析工作就已经结束了,下面贴一个例子。
例子:通过如下标签,来解析一个User类
xml 标签:
<example:user id="u1" userName="alex" email="alex@163.com" password="alex123" />
User 类:
public class User { private String id; private String userName; private String email; private String password; public String getId() { return id; } public void setId(String id) { this.id = id; } 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; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "id='" + id + '\'' + ", userName='" + userName + '\'' + ", email='" + email + '\'' + ", password='" + password + '\'' + '}'; } }
handler 类:
public class UserNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("user", new UserBeanDefinitionParser()); } }
paser 类 ,注:我这里继承的是 AbstractSimpleBeanDefinitionParser ,其提供了同名的 xml 属性 到 对象属性的设置:
public class UserBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser { @Override protected Class<?> getBeanClass(Element element) { return User.class; } @Override protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { // id 默认会当做 bean 的名称,而不是赋值给 user 对象,可以手动进行处理。 String id = element.getAttribute("id"); System.out.println(id); builder.addPropertyValue("id", element.getAttribute("id")); super.doParse(element, parserContext, builder); } }
resources#META-INF#spring.handlers:
http\://www.example.com/schema/user=study.yan.models.selftag.UserNamespaceHandler
spring-context.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:example="http://www.yan.com/schema/user" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd "> <example:user id="u1" userName="alex" email="alex@163.com" password="alex123" /> </beans>
main.java,main函数中的 NoValidClassPathXmlApplicationContext 是我自定义的。为什么要自定义呢?因为默认 Spirng 启动的时候会验证 xml 格式,验证 xml 格式需要 xsd 或者 dtd文件,我们之后会加上。
public static void main(String[] args) { ApplicationContext cxt = new NoValidClassPathXmlApplicationContext("spring-context.xml"); User user = (User) cxt.getBean("u1"); System.out.println(user); }
NoValidClassPathXmlApplicationContext.java:
public class NoValidClassPathXmlApplicationContext extends ClassPathXmlApplicationContext { public NoValidClassPathXmlApplicationContext(String configLocation) throws BeansException { super(configLocation); } @Override protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) { super.setValidating(false); // 设置不需要验证 super.initBeanDefinitionReader(reader); } }