操作系统寒武纪 - 会让企业IT高兴吗?
1002
2023-01-07
如何使用Spring自定义Xml标签
目录前言正文自定义NameSpaceHandler自定义schemaParserDecorator总结
前言
在早期基于Xml配置的Spring Mvc项目中,我们往往会使用
正文
在分析这个自定义标签的解析机制前,我先提前剧透这个自定义标签是通过哪个强大的类来解析的吧,就是隶属于spring-context包下的ComponentScanBeanDefinitionParser,这个类在Springboot扫描Bean的过程中也扮演了重要角色。
既然知道了是这个类解析的,那么我们可以通过idea强大的搜索功能来搜它的引用之处了,这边JAVKus就截图如下:
可以看到这里面初始化了8个带Parser后缀的各种Parser,从方法registerBeanDefinitionParser看出Spring是通过这个ContextNamespaceHandler来完成对以 那么我们自己是否可以自定义一个NamespaceHandler来注册我们自定义的标签解析器呢?答案是肯定的。 自定义NameSpaceHandler final class TestNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { //注册parser registerBeanDefinitionParser("testBean", new TestBeanDefinitionParser()); registerBeanDefinitionParser("person", new PersonDefinitionParser()); //注册element的 decorater registerBeanDefinitionDecorator("set", new PropertyModifyingBeanDefinitionDecorator()); registerBeanDefinitionDecorator("debug", new DebugBeanDefinitionDecorator()); //注册 attr的 decorator registerBeanDefinitionDecoratorForAttribute("object-name", new ObjectNameBeanDefinitionDecorator()); } 到这里大家可能会有个疑问,这个NameSpaceHandler是怎么使用的呢?大家如果看了我之前写的文章,那就会知道有一种方式可以配置我们自定义的NamespaceHandler. public class CustomXmlApplicationContext extends AbstractXmlApplicationContext { private static final String CLASSNAME = CustomXmlApplicationContext.class.getSimpleName(); private static final String FQ_PATH = "org/wonder/frame/customBean"; private static final String NS_PROPS = format("%s/%s.properties", FQ_PATH, CLASSNAME); public CustomXmlApplicationContext(String... configLocations) { setConfigLocations(configLocations); refresh(); } @Override protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) { super.initBeanDefinitionReader(reader); //1.指定resolver的 handlerMappingsLocation 就是 NamespaceHandler的 配置文件路径 NamespaceHandlerResolver resolver = new DefaultNamespaceHandlerResolver(this.getClassLoader(), NS_PROPS); //2.设置resolver reader.setNamespaceHandlerResolver(resolver); //3.设置验证模式 reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD); //4.设置entityResolver reader.setEntityResolver(new CustomSchemaResolver()); } 可以看到我们在初始化BeanDefinitionReader的时候我们可以设置NamespaceHandlerResolver并且配置它的NamespaceHandler文件路径。那这个NamespaceHandler配置文件应该怎么写呢? http\://john.com/resource=org.wonder.frame.customBean.TestNamespaceHandler 就一行配置,但是这里有两点要注意: http\://john.com/resource要和xsd的targetNamspace一致。 http\://john.com/resource要和xml配置文件中的自定义namespace一致。 从代码里看出来我们解析自定义标签的时候其实是还需要自定义schema才能完成的。 自定义schema private static final String CLASSNAME = CustomXmlApplicationContext.class.getSimpleName(); private static final String FQ_PATH = "org/wonder/frame/customBean"; private static final String TEST_XSD = format("%s/%s.xsd", FQ_PATH, CLASSNAME); private final class CustomSchemaResolver extends PluggableSchemaResolver { public CustomSchemaResolver() { super(CustomXmlApplicationContext.this.getClassLoader()); } @Override public InputSource resolveEntity(String publicId, String systemId) throws IOException { InputSource source = super.resolveEntity(publicId, systemId); if (source == null) { try{ //todo 指定了xsd路径 Resource resource = new ClassPathResource(TEST_XSD); source = new InputSource(resource.getInputStream()); source.setPublicId(publicId); source.setSystemId(systemId); return source; } catch (FileNotFoundException ex){ } } return null; } } 这里我们也通过ClassPathResource设置了自定义的xsd文件路径。我们来看看xsd文件长啥样: xmlns:xsd="http://w3.org/2001/XMLSchema" targetNamespace="http://john.com/resource" elementFormDefault="qualified"> Parser 我们先来分析下TestBeanDefinitionParser和PersonDefinitionParser这两者有啥区别: TestBeanDefinitionParser直接实现了BeanDefinitionParser接口,内部直接定义一个RootBeanDefinition并且注册。 private static class TestBeanDefinitionParser implements BeanDefinitionParser { @Override public BeanDefinition parse(Element element, ParserContext parserContext) { RootBeanDefinition definition = new RootBeanDefinition(); definition.setBeanClass(CustomBean.class); MutablePropertyValues mpvs = new MutablePropertyValues(); mpvs.add("name", element.getAttribute("name")); mpvs.add("age", element.getAttribute("age")); //1.设置beanDefinition的 属性 propertyValues definition.setPropertyValues(mpvs); //2.获取到beanDefinition的 registry parserContext.getRegistry().registerBeanDefinition(element.getAttribute("id"), definition); return null; } } PersonDefinitionParser继承自AbstractSingleBeanDefinitionParser抽象类,内部使用BeanDefinitionBuilder构造器来完成BeanDefinition的创建。 private static final class PersonDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class> getBeanClass(Element element) { return CustomBean.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder builder) { builder.addPropertyValue("name", element.getAttribute("name")); builder.addPropertyValue("age", element.getAttribute("age")); } } Decorator 我们看到在NameSpaceHandler中我们除了parser外还可以定义自定义元素的decorator和自定义attribute的decorator,那这两个decorator是用来干嘛的呢?我们先来看下上述代码中的PropertyModifyingBeanDefinitionDecorator。 private static class PropertyModifyingBeanDefinitionDecorator implements BeanDefinitionDecorator { @Override public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) { Element element = (Element) node; //1.获取BeanDefinition BeanDefinition def = definition.getBeanDefinition(); MutablePropertyValues mpvs = (def.getPropertyValues() == null) ? new MutablePropertyValues() : def.getPropertyValues(); mpvs.add("name", element.getAttribute("name")); mpvs.add("age", element.getAttribute("age")); ((AbstractBeanDefinition) def).setPropertyValues(mpvs); return definition; } } 从decorate方法内部看出这个decorator是用来给我们的BeanDefinition来添加属性的。这样一来我们就可以在Xml配置中定义元素的属性值,比如下图示例: xmlns:xsi="http://w3.org/2001/XMLSchema-instance" xmlns:test="http://john.com/resource" xmlns:util="http://springframework.org/schema/util" xsi:schemaLocation="http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans-2.0.xsd http://springframework.org/schema/util http://springframework.org/schema/util/spring-util-2.0.xsd http://john.com/resource http://john.com/resource/org/wonder/frame/customBean/CustomXmlApplicationContext.xsd" default-lazy-init="true"> 我们看到testBean这个自定义标签定义了两个属性name和age。之后我们在使用这个testBean的时候就可以获取到它的name和age属性了。 CustomBean bean = (CustomBean) beanFactory.getBean("testBean"); System.out.println("name is:" +bean.getName() +" and age is:"+ bean.getAge()); 那么ObjectNameBeanDefinitionDecorator这个attribute的Decorator是干嘛的呢?看如下示例 我们可以为这个Bean添加自定义Attribute,那么添加了这个Attribute我们怎么使用呢?看如下示例: BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("decorateWithAttribute"); assertEquals("foo", beanDefinition.getAttribute("objectName")); 我们通过BeanDefinition的getAttribute就能获取到这个attribute值。 从Spring源码得知BeanDefinition扩展了AttributeAccessor接口,这个接口是用于附加和访问Bean元数据的通用的接口。直接实现这个接口的是AttributeAccessorSupport类。这个类里定义了名为attributes 的LinkedHashMap。 总结 Spring通过自定义标签和自定义属性实现了很多扩展功能,很多我们常用的Spring配置内部都是通过它来完成的。 以上就是如何使用Spring自定义Xml标签的详细内容,更多关于使用Spring自定义Xml标签的资料请关注我们其它相关文章!
那么我们自己是否可以自定义一个NamespaceHandler来注册我们自定义的标签解析器呢?答案是肯定的。
自定义NameSpaceHandler
final class TestNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
//注册parser
registerBeanDefinitionParser("testBean", new TestBeanDefinitionParser());
registerBeanDefinitionParser("person", new PersonDefinitionParser());
//注册element的 decorater
registerBeanDefinitionDecorator("set", new PropertyModifyingBeanDefinitionDecorator());
registerBeanDefinitionDecorator("debug", new DebugBeanDefinitionDecorator());
//注册 attr的 decorator
registerBeanDefinitionDecoratorForAttribute("object-name", new ObjectNameBeanDefinitionDecorator());
}
到这里大家可能会有个疑问,这个NameSpaceHandler是怎么使用的呢?大家如果看了我之前写的文章,那就会知道有一种方式可以配置我们自定义的NamespaceHandler.
public class CustomXmlApplicationContext extends AbstractXmlApplicationContext {
private static final String CLASSNAME = CustomXmlApplicationContext.class.getSimpleName();
private static final String FQ_PATH = "org/wonder/frame/customBean";
private static final String NS_PROPS = format("%s/%s.properties", FQ_PATH, CLASSNAME);
public CustomXmlApplicationContext(String... configLocations) {
setConfigLocations(configLocations);
refresh();
}
@Override
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
super.initBeanDefinitionReader(reader);
//1.指定resolver的 handlerMappingsLocation 就是 NamespaceHandler的 配置文件路径
NamespaceHandlerResolver resolver = new DefaultNamespaceHandlerResolver(this.getClassLoader(), NS_PROPS);
//2.设置resolver
reader.setNamespaceHandlerResolver(resolver);
//3.设置验证模式
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_XSD);
//4.设置entityResolver
reader.setEntityResolver(new CustomSchemaResolver());
}
可以看到我们在初始化BeanDefinitionReader的时候我们可以设置NamespaceHandlerResolver并且配置它的NamespaceHandler文件路径。那这个NamespaceHandler配置文件应该怎么写呢?
http\://john.com/resource=org.wonder.frame.customBean.TestNamespaceHandler
就一行配置,但是这里有两点要注意:
http\://john.com/resource要和xsd的targetNamspace一致。
http\://john.com/resource要和xml配置文件中的自定义namespace一致。
从代码里看出来我们解析自定义标签的时候其实是还需要自定义schema才能完成的。
自定义schema
private static final String CLASSNAME = CustomXmlApplicationContext.class.getSimpleName();
private static final String FQ_PATH = "org/wonder/frame/customBean";
private static final String TEST_XSD = format("%s/%s.xsd", FQ_PATH, CLASSNAME);
private final class CustomSchemaResolver extends PluggableSchemaResolver {
public CustomSchemaResolver() {
super(CustomXmlApplicationContext.this.getClassLoader());
}
@Override
public InputSource resolveEntity(String publicId, String systemId) throws IOException {
InputSource source = super.resolveEntity(publicId, systemId);
if (source == null) {
try{
//todo 指定了xsd路径
Resource resource = new ClassPathResource(TEST_XSD);
source = new InputSource(resource.getInputStream());
source.setPublicId(publicId);
source.setSystemId(systemId);
return source;
}
catch (FileNotFoundException ex){
}
}
return null;
}
}
这里我们也通过ClassPathResource设置了自定义的xsd文件路径。我们来看看xsd文件长啥样:
xmlns:xsd="http://w3.org/2001/XMLSchema" targetNamespace="http://john.com/resource" elementFormDefault="qualified">
xmlns:xsd="http://w3.org/2001/XMLSchema"
targetNamespace="http://john.com/resource"
elementFormDefault="qualified">
Parser
我们先来分析下TestBeanDefinitionParser和PersonDefinitionParser这两者有啥区别:
TestBeanDefinitionParser直接实现了BeanDefinitionParser接口,内部直接定义一个RootBeanDefinition并且注册。
private static class TestBeanDefinitionParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition definition = new RootBeanDefinition();
definition.setBeanClass(CustomBean.class);
MutablePropertyValues mpvs = new MutablePropertyValues();
mpvs.add("name", element.getAttribute("name"));
mpvs.add("age", element.getAttribute("age"));
//1.设置beanDefinition的 属性 propertyValues
definition.setPropertyValues(mpvs);
//2.获取到beanDefinition的 registry
parserContext.getRegistry().registerBeanDefinition(element.getAttribute("id"), definition);
return null;
}
}
PersonDefinitionParser继承自AbstractSingleBeanDefinitionParser抽象类,内部使用BeanDefinitionBuilder构造器来完成BeanDefinition的创建。
private static final class PersonDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class> getBeanClass(Element element) {
return CustomBean.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
builder.addPropertyValue("name", element.getAttribute("name"));
builder.addPropertyValue("age", element.getAttribute("age"));
}
}
Decorator
我们看到在NameSpaceHandler中我们除了parser外还可以定义自定义元素的decorator和自定义attribute的decorator,那这两个decorator是用来干嘛的呢?我们先来看下上述代码中的PropertyModifyingBeanDefinitionDecorator。
private static class PropertyModifyingBeanDefinitionDecorator implements BeanDefinitionDecorator {
@Override
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
Element element = (Element) node;
//1.获取BeanDefinition
BeanDefinition def = definition.getBeanDefinition();
MutablePropertyValues mpvs = (def.getPropertyValues() == null) ? new MutablePropertyValues() : def.getPropertyValues();
mpvs.add("name", element.getAttribute("name"));
mpvs.add("age", element.getAttribute("age"));
((AbstractBeanDefinition) def).setPropertyValues(mpvs);
return definition;
}
}
从decorate方法内部看出这个decorator是用来给我们的BeanDefinition来添加属性的。这样一来我们就可以在Xml配置中定义元素的属性值,比如下图示例:
xmlns:xsi="http://w3.org/2001/XMLSchema-instance" xmlns:test="http://john.com/resource" xmlns:util="http://springframework.org/schema/util" xsi:schemaLocation="http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans-2.0.xsd http://springframework.org/schema/util http://springframework.org/schema/util/spring-util-2.0.xsd http://john.com/resource http://john.com/resource/org/wonder/frame/customBean/CustomXmlApplicationContext.xsd" default-lazy-init="true">
xmlns:xsi="http://w3.org/2001/XMLSchema-instance"
xmlns:test="http://john.com/resource"
xmlns:util="http://springframework.org/schema/util"
xsi:schemaLocation="http://springframework.org/schema/beans http://springframework.org/schema/beans/spring-beans-2.0.xsd
http://springframework.org/schema/util http://springframework.org/schema/util/spring-util-2.0.xsd
http://john.com/resource http://john.com/resource/org/wonder/frame/customBean/CustomXmlApplicationContext.xsd"
default-lazy-init="true">
我们看到testBean这个自定义标签定义了两个属性name和age。之后我们在使用这个testBean的时候就可以获取到它的name和age属性了。
CustomBean bean = (CustomBean) beanFactory.getBean("testBean");
System.out.println("name is:" +bean.getName() +" and age is:"+ bean.getAge());
那么ObjectNameBeanDefinitionDecorator这个attribute的Decorator是干嘛的呢?看如下示例
我们可以为这个Bean添加自定义Attribute,那么添加了这个Attribute我们怎么使用呢?看如下示例:
BeanDefinition beanDefinition = this.beanFactory.getBeanDefinition("decorateWithAttribute");
assertEquals("foo", beanDefinition.getAttribute("objectName"));
我们通过BeanDefinition的getAttribute就能获取到这个attribute值。
从Spring源码得知BeanDefinition扩展了AttributeAccessor接口,这个接口是用于附加和访问Bean元数据的通用的接口。直接实现这个接口的是AttributeAccessorSupport类。这个类里定义了名为attributes 的LinkedHashMap。
总结
Spring通过自定义标签和自定义属性实现了很多扩展功能,很多我们常用的Spring配置内部都是通过它来完成的。
以上就是如何使用Spring自定义Xml标签的详细内容,更多关于使用Spring自定义Xml标签的资料请关注我们其它相关文章!
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~