在Spring-Framework
下新建Module
先看下工程结构
代码
Teacher
package com.fy.test.model;
public class Teacher {
private Student student;
public Teacher(Student student) {
this.student = student;
}
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
@Override
public String toString() {
return "Teacher{" +
"Student=" + student +
'}';
}
}
Student
package com.fy.test.model;
public class Student {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
开始自定义
1. 先定义自己的标签fy.xsd
说明:
http://fy.custom.com/schema/fy
自定义的,可以随便命名,但是要保持前后统一哦
可以参考org/springframework/beans/factory/xml/spring-beans.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://fy.custom.com/schema/fy"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://fy.custom.com/schema/fy"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<!--在这里模仿spring的bean标签的定义,自己写一个-->
<xsd:element name="bean">
<xsd:complexType>
<xsd:complexContent>
<!--声明id-->
<xsd:extension base="identifiedType">
<!--声明子元素-->
<xsd:group ref="beanElements"/>
<!--声明其他属性-->
<xsd:attributeGroup ref="beanAttributes"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:attributeGroup name="beanAttributes">
<xsd:attribute name="name" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
Can be used to create one or more aliases illegal in an (XML) id.
Multiple aliases can be separated by any number of spaces, commas,
or semi-colons (or indeed any mixture of the three).
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="class" type="xsd:string">
<xsd:annotation>
<xsd:documentation source="java:java.lang.Class"><![CDATA[
The fully qualified name of the bean's class, except if it serves only
as a parent definition for child bean definitions.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:anyAttribute namespace="##other" processContents="lax"/>
</xsd:attributeGroup>
<xsd:complexType name="identifiedType" abstract="true">
<xsd:annotation>
<xsd:documentation><![CDATA[
The unique identifier for a bean. The scope of the identifier
is the enclosing bean factory.
]]></xsd:documentation>
</xsd:annotation>
<xsd:attribute name="id" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[
The unique identifier for a bean. A bean id may not be used more than once
within the same <beans> element.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>
<xsd:group name="beanElements">
<xsd:sequence>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element ref="property"/>
<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
</xsd:choice>
</xsd:sequence>
</xsd:group>
<xsd:element name="property">
<xsd:complexType>
<xsd:attribute name="name" type="xsd:string"/>
<xsd:attribute name="value" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
2. 实现自定义的解析处理类,解析自己定义的标签
自定义标签解析类,通过继承
AbstractSingleBeanDefinitionParser
的方式
package com.fy.test.handler;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
public class FYCustomBeanParser extends AbstractSingleBeanDefinitionParser {
public static final String CLASS_ATTRIBUTE = "class";
@Override
protected String getBeanClassName(Element element) {
if (element.hasAttribute(CLASS_ATTRIBUTE)){
return element.getAttribute(CLASS_ATTRIBUTE);
}
return null;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
// 因为我定义的标签的子元素是property,所以可以直接调用spring的方法来解析
parserContext.getDelegate().parsePropertyElements(element, builder.getBeanDefinition());
}
}
自定义标签解析处理类,用来指定自定义标签的用哪个解析类来解析,通过继承
NamespaceHandlerSupport
的方式
package com.fy.test.handler;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class FYCustomBeanHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// 指定FYCustomBeanParser来解析自定义的bean标签
registerBeanDefinitionParser("bean", new FYCustomBeanParser());
}
}
3. 在META-INF
中做一些配置,让Spring在解析自定义的标签时,通过自己定义的解析类进行解析
spring.handlers
http\://fy.custom.com/schema/fy=com.fy.test.handler.FYCustomBeanHandler
spring.schemas
http\://fy.custom.com/schema/fy.xsd=config/fy.xsd
4. 使用自定义标签fy-beans.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:fy="http://fy.custom.com/schema/fy"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://fy.custom.com/schema/fy http://fy.custom.com/schema/fy.xsd">
<bean id="teacher" class="com.fy.test.model.Teacher">
<property name="student" ref="student"/>
</bean>
<bean id="student" class="com.fy.test.model.Student">
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
<!--自定义的bean标签-->
<fy:bean id="student" class="com.fy.test.model.Student">
<fy:property name="name" value="李四"/>
<fy:property name="age" value="20"/>
</fy:bean>
</beans>
5. 测试
package com.fy.test;
import com.fy.test.model.Student;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class FYCustomTest {
private final static String FY_BEANS_XML = "fy-beans.xml";
@Test
public void testGetBean() {
ApplicationContext context = new ClassPathXmlApplicationContext(FY_BEANS_XML);
Student student = context.getBean("student", Student.class);
System.out.println(student);
}
}
6. 结果OK
说明
spring-test-fy.gradle
需要添加spring-context
的依赖
可参考
spring-test.gradle
我是在Spring源码5.2.x版本中测试的,一直报这个错,始终编译不过,一度怀疑人生。
把
gradle
下的docs.gradle
文件中第220
行的校验注释掉,就可以了
自定义标签解析的原理
重点在这个resolve(String namespaceUri)
方法里
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
在解析的时候
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
String localName = parserContext.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}