概述自定义标签建立组件AbstractSingleBeanDefinitionParser 实现方式定义 XSD 文件Parser 类Handler 类Spring.handlers和Spring.schemas建立测试配置文件测试BeanDefinitionParser 实现方式定义 XSD 文件Parser 类Handler 类Spring.handlers和Spring.schemas建立测试配置文件自定义属性自定义子标签总结参考文献java
前景:常用一些依赖于 Spring 的组件时,发现能够经过自定义配置 Spring 的标签来实现插件的注入,例如数据库源的配置,Mybatis 的配置等。那么这些 Spring 标签是如何自定义配置的?学习 Spring 标签的自定义配置为之后实现分布式服务框架作技术储备。node
技术分析:Spring 的标签配置是经过 XML 来实现的,经过 XSD(xml Schema Definition)来定义元素,属性,数据类型等。 web
Spring 在解析 xml 文件中的标签的时候会区分当前的标签是四种基本标签(import、alias、bean和beans)仍是自定义标签,若是是自定义标签,则会按照自定义标签的逻辑解析当前的标签。另外,即便是 bean 标签,其也可使用自定义的属性或者使用自定义的子标签。本文将对自定义标签和自定义属性的使用方式进行讲解,而且会从源码的角度对自定义标签和自定义属性的实现方式进行讲解。 spring
扩展 Spring 自定义标签配置通常须要如下几个步骤:数据库
下面就按照上面的步骤来实现一个自定义标签组件。ruby
Car.javabash
public class Car {
private int maxSpeed ;
private double price ;
private String brand ;
private String color;
public Car() {
System.out.println("调用Car类的无参构造函数");
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Car{" +
"maxSpeed=" + maxSpeed +
", price=" + price +
", brand='" + brand + '\'' +
", color='" + color + '\'' +
'}';
}
}
复制代码
该文件命名为 org.xsdapp
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns="http://hresh.com/schema/org"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://hresh.com/schema/org"
elementFormDefault="qualified">
<xsd:element name="car">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="maxSpeed" type="xsd:integer" />
<xsd:attribute name="price" type="xsd:double" />
<xsd:attribute name="brand" type="xsd:string" />
<xsd:attribute name="color" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:schema>
复制代码
在上述 XSD 文件中描述了一个新的 targetNamespace,并在这个空间里定义一个 name 为 car 的 element。car 里面有五个 attribute, 须要注意的是,其中四个属性与咱们的 Car 对象的属性没有直接的关系,这里只是一个 XSD文件的声明,以表征Spring 的 application.xml 文件中使用当前命名空间时可使用的标签属性。命名是否一致根据我的喜爱来,id 属性至关于标签的 id 属性,用来标识每一个自定义标签。框架
定义一个 Parser 类,该类继承 AbstractSingleBeanDefinitionParser ,并实现 getBeanClass()
和 doParse()
两个方法,主要是用于解析 XSD 文件中的定义和组件定义。分布式
public class CarBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String brand = element.getAttribute("brand");
String color = element.getAttribute("color");
double price = Double.valueOf(element.getAttribute("price"));
int maxSpeed = Integer.valueOf(element.getAttribute("maxSpeed"));
if(StringUtils.hasText(brand)){
builder.addPropertyValue("brand",brand);
}
if(StringUtils.hasText(color)){
builder.addPropertyValue("color",color);
}
builder.addPropertyValue("price",price);
builder.addPropertyValue("maxSpeed",maxSpeed);
}
@Override
protected Class<?> getBeanClass(Element element) {
return Car.class;
}
}
复制代码
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("car",new CarBeanDefinitionParser());
}
}
复制代码
编写 Spring.handlers 和 Spring.schemas 文件,默认位置放在工程的META-INF文件夹下。
Spring.handlers
http\://hresh.com/schema/org=com.msdn.schema.MyNamespaceHandler
复制代码
Spring.schemas
http\://hresh.com/schema/org.xsd=META-INF/org.xsd
复制代码
而 Spring 加载自定义的大体流程是遇到自定义标签而后 就去 Spring.handlers 和 Spring.schemas 中去找对应的 handler 和 XSD ,默认位置是 META-INF 下,进而有找到对应的handler以及解析元素的 Parser ,从而完成了整个自定义元素的解析,也就是说 Spring 将向定义标签解析的工做委托给了 用户去实现。
通过上面几个步骤,就可使用自定义的标签了。在 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:myTag="http://hresh.com/schema/org"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://hresh.com/schema/org http://hresh.com/schema/org.xsd">
<myTag:car id="car2" price="56000" maxSpeed="240" brand="宝马" color="银色" />
</beans>
复制代码
xmlns:myTag 表示 myTag 的命名空间是 http://hresh.com/schema/org
@Test
public void otherGetBean(){
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
Car car = (Car) context.getBean("car2");
System.out.println(car);
}
复制代码
该文件命名为 soa.xsd。
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns="http://hresh.com/schema/soa"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://hresh.com/schema/soa"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<xsd:element name="xxx" >
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="maxSpeed" type="xsd:integer" />
<xsd:attribute name="price" type="xsd:double" />
<xsd:attribute name="brand" type="xsd:string" />
<xsd:attribute name="color" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
复制代码
在上述 XSD 文件中描述了一个新的 targetNamespace,并在这个空间里定义一个 name 为 xxx 的 element。xxx里面有四个 attribute,对应 Car 类中包含的属性。因为<xsd:extension base="beans:identifiedType">
标签的缘故,默认带有 id 属性,因此不须要另外添加。
定义一个 Parser 类,该类实现 BeanDefinitionParser,并实现 构造方法
和 parse()
两个方法。主要是用于解析 XSD 文件中的定义和组件定义。
public class CarParser implements BeanDefinitionParser {
private Class<?> beanclass;
public CarParser(Class<?> beanclass) {
this.beanclass = beanclass;
}
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(beanclass);
beanDefinition.setLazyInit(false);
String brand = element.getAttribute("brand");
String color = element.getAttribute("color");
double price = Double.valueOf(element.getAttribute("price"));
int maxSpeed = Integer.valueOf(element.getAttribute("maxSpeed"));
beanDefinition.getPropertyValues().add("brand",brand);
beanDefinition.getPropertyValues().add("color",color);
beanDefinition.getPropertyValues().add("price",price);
beanDefinition.getPropertyValues().add("maxSpeed",maxSpeed);
BeanDefinitionRegistry beanDefinitionRegistry = parserContext.getRegistry();
// beanDefinitionRegistry.registerBeanDefinition(beanclass.getName(),beanDefinition);//注册bean到BeanDefinitionRegistry中
String id = element.getAttribute("id");
beanDefinitionRegistry.registerBeanDefinition(id,beanDefinition);
return beanDefinition;
}
}
复制代码
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("xxx",new CarParser(Car.class));
}
}
复制代码
编写 Spring.handlers 和 Spring.schemas 文件,默认位置放在工程的META-INF文件夹下。
Spring.handlers
http\://hresh.com/schema/soa=com.msdn.schema.MyNamespaceHandler
复制代码
Spring.schemas
http\://hresh.com/schema/soa.xsd=META-INF/soa.xsd
复制代码
<?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:myTag="http://hresh.com/schema/soa"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://hresh.com/schema/soa http://hresh.com/schema/soa.xsd">
<myTag:xxx id="car2" price="56000" maxSpeed="240" brand="宝马" color="银色" />
</beans>
复制代码
测试代码同上。
项目总体文件目录以下:
自定义属性的定义方式和自定义标签很是类似,其主要也是进行命名空间和转换逻辑的定义。假设咱们有一个 User 对象,咱们须要使用自定义标签为其添加一个描述属性。以下是 User 对象的定义:
public class User {
private String name;
private String desc;
public User() {
System.out.println("user无参构造方法");
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
复制代码
这里咱们自定义 desc 属性,对应的 XSD 文件定义以下:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns="http://hresh.com/schema/user"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://hresh.com/schema/user"
elementFormDefault="qualified">
<xsd:attribute name="desc" type="xsd:string" />
</xsd:schema>
复制代码
须要注意的是,和自定义标签不一样的是,自定义标签是将处理逻辑注册到 parsers 对象中,这里自定义属性是将处理逻辑注册到 attributeDecorators 中。以下 UserDefinitionDecorator 的逻辑:
public class UserDefinitionDecorator implements BeanDefinitionDecorator {
@Override
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder beanDefinitionHolder, ParserContext parserContext) {
String desc = ((Attr)node).getValue();
beanDefinitionHolder.getBeanDefinition().getPropertyValues().add("desc",desc);
return beanDefinitionHolder;
}
}
复制代码
能够看到,对于 desc 的处理逻辑就是获取当前定义的属性的值,因为知道其是当前标签的一个属性,于是能够将其强转为一个 Attr 类型的对象,并获取其值,而后将其添加到指定的 BeandDefinitionHolder 中。这里须要注意的是,自定义标签继承的是 AbstractSingleBeanDefinitionParser 类,其实是实现的 BeanDefinitionParser 接口,而自定义属性实现的则是 BeanDefinitionDecorator 接口。
对应 Handler 类的定义以下:
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// registerBeanDefinitionParser("xxx",new UserParser(User.class));
registerBeanDefinitionDecoratorForAttribute("desc",new UserDefinitionDecorator());
}
}
复制代码
编写 Spring.handlers 和 Spring.schemas 文件,默认位置放在工程的META-INF文件夹下。
Spring.handlers
http\://hresh.com/schema/user=com.msdn.schema.MyNamespaceHandler
复制代码
Spring.schemas
http\://hresh.com/schema/user.xsd=META-INF/user-desc.xsd
复制代码
最后配置 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
xmlns:myTag="http://hresh.com/schema/user">
<bean id="user" class="com.msdn.bean.User" myTag:desc="a good boy">
<property name="name" value="hresh" />
</bean>
</beans>
复制代码
测试代码以下:
@Test
public void parseTest(){
ApplicationContext context = new ClassPathXmlApplicationContext("application_context.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
复制代码
执行结果为:
user无参构造方法
User{name='hresh', desc='a good boy'}
复制代码
对于自定义子标签的使用,其与自定义标签的使用很是类似,不过须要注意的是,根据对自定义属性的源码解析,咱们知道自定义子标签并非自定义标签,自定义子标签只是起到对其父标签所定义的 bean 的一种装饰做用,于是自定义子标签的处理逻辑定义与自定义标签主要有两点不一样:
NamespaceHandler.init()
方法中注册自定义子标签的处理逻辑时须要使用registerBeanDefinitionDecorator(String, BeanDefinitionDecorator)
方法;这里我就把 Parser 类和 Handler 类的代码实现列出来。
public class MyNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// registerBeanDefinitionParser("xxx",new UserParser(User.class));//自定义标签
// registerBeanDefinitionDecoratorForAttribute("desc",new UserDefinitionDecorator());//自定义属性
registerBeanDefinitionDecorator("node",new UserDefinitionDecorator(User.class));//自定义子标签
}
}
复制代码
public class UserDefinitionDecorator implements BeanDefinitionDecorator {
private Class<?> beanclass;
public UserDefinitionDecorator(Class<?> beanclass) {
this.beanclass = beanclass;
}
@Override
public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder beanDefinitionHolder, ParserContext parserContext) {
// String desc = ((Attr)node).getValue();
// beanDefinitionHolder.getBeanDefinition().getPropertyValues().add("desc",desc);
// return beanDefinitionHolder;
BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
String name = ((Element)node).getAttribute("name");
String value = ((Element)node).getAttribute("value");
beanDefinition.getPropertyValues().add(name,value);
return beanDefinitionHolder;
}
}
复制代码
还有 descTag.xsd 文件
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema
xmlns="http://hresh.com/schema/descTag"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://hresh.com/schema/descTag"
elementFormDefault="qualified">
<xsd:import namespace="http://www.springframework.org/schema/beans" />
<!--<xsd:complexType name="elementname1complexType">
<xsd:attribute name="name" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ The elementname1 name. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="value" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[ The elementname1 name. ]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:element name="node" type="elementname1complexType">
<xsd:annotation>
<xsd:documentation><![CDATA[ elementname1的文档 ]]></xsd:documentation>
</xsd:annotation>
</xsd:element>-->
<xsd:element name="node" >
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="value" type="xsd:string" />
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
复制代码
文件中的两种定义方式均可以,第二种更加简洁一些。
本文主要对自定义标签,自定义属性和自定义子标签的使用方式进行了讲解,有了对自定义标签的理解,咱们能够在 Spring 的 xml 文件中根据本身的须要实现本身的处理逻辑。另外须要说明的是,Spring 源码中也大量使用了自定义标签,好比 Spring 的 AOP 的定义,其标签为。咱们知道,Spring 默认只会处理import、alias、bean 和 beans 四种标签,对于其他的标签,如咱们所熟知的事务处理标签,这些都是使用自定义标签实现的。
https://my.oschina.net/zhangxufeng/blog/1815705