Spring原始碼學習-自定義標籤實踐及原理
阿新 • • 發佈:2021-06-30
在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;
}