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 + '}'; } }
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>
自定义标签解析类,通过继承
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()); } }
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
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>
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); } }
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; }