Spring 配置文件自定義標簽的前置條件 在上一篇文章https://www.cnblogs.com/redwinter/p/16165274.html Spring BeanFactory的創建過程中瞭解了BeanDefinition的載入和BeanFactory的創建,並且提到了Spring留 ...
Spring 配置文件自定義標簽的前置條件
在上一篇文章https://www.cnblogs.com/redwinter/p/16165274.html Spring BeanFactory
的創建過程中瞭解了BeanDefinition
的載入和BeanFactory
的創建,並且提到了Spring
留了一個擴展點就是用戶可以自定義標簽進行解析BeanDefinition
。
基於Spring
源碼在處理定製的標簽時是通過定製的命名空間處理器和xsd
文件進行解析的,在spring
的classpath
下的META-INF/spring.schemas
和META-INF/spring.handlers
,並且需要將標簽的解析器註冊到BeanDefinition
的解析器中,這樣說起來比較抽象,接下來我們自己定義一個標簽就明瞭了。
定義標簽屬性類
創建一個需要解析的標簽的屬性,比如在Spring
配置文件中經常看到的<context:component-scan base-package="com.redwinter.test"/>
,這裡的component-scan
就是標簽屬性。
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class Redwinter {
private String username;
private String email;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
定義一個Redwinter
類,裡面三個屬性,當然你可以自己定義你需要的屬性,我這裡就隨便寫啦。
定義標簽屬性解析器類
定義好標簽的屬性之後就需要定義一個解析器對這些屬性進行解析,定義解析器需要繼承AbstractSingleBeanDefinitionParser
,這個類是實現了BeanDefinitionParser
的類,他下麵有很多實現類,一般來說我們的Bean都是單例的,那就繼承AbstractSingleBeanDefinitionParser
即可。
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class RedwinterBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return Redwinter.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
/**
* 自定義解析xml的自定義欄位,並做相應的其他處理
*/
String username = element.getAttribute("username");
String email = element.getAttribute("email");
String password = element.getAttribute("password");
if (StringUtils.hasText(username)){
builder.addPropertyValue("username",username);
}
if (StringUtils.hasText(email)){
builder.addPropertyValue("email",email);
}
if (StringUtils.hasText(password)){
builder.addPropertyValue("password",password);
}
}
}
這個解析器主要是重寫了父類的兩個方法,一個是getBeanClass
用於返回對應的標簽屬性類,一個是解析屬性doParser
,這裡我只是從element
中獲取出來然後進行了下判斷在加入到屬性值中,當然這裡你可以自定義自己的邏輯處理。
定義命名空間處理器類
定義命名空間處理器需要繼承NamespaceHandlerSupport,然後重寫他的init方法,將解析器註冊進去,這個解析器就是上面定義的用來解析標簽屬性的解析器。
/**
* @author <a href="https://www.cnblogs.com/redwinter/">redwinter</a>
* @since 1.0
**/
public class RedwinterNameSpaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// 這裡的屬性必須和xsd中指定的屬性一致,否則報錯
//org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Cannot locate BeanDefinitionParser for element [dl]
registerBeanDefinitionParser("dl",new RedwinterBeanDefinitionParser());
}
}
這裡需要註意的是,進行註冊時需要指定一個elementName
,這個值必須和xml中定義的名稱一致,否者的話就會報如下錯:
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Cannot locate BeanDefinitionParser for element [dl]
我這裡定義的元素名稱叫dl。
定義xsd文件
xsd文件就是spring進行xml解析時解析的標簽,當然你可以定義dtd文件,不過現在一般都用xsd文件,我這裡命名為redwinter.xsd,完整文件如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.redwinter.com/schema/redwinter"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.redwinter.com/schema/redwinter"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:element name="dl">
<xsd:complexType>
<xsd:attribute name="id" type="xsd:string"/>
<xsd:attribute name="username" type="xsd:string" use="required"/>
<xsd:attribute name="email" type="xsd:string" use="required"/>
<xsd:attribute name="password" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:schema>
這裡有幾個點需要註意: schema
標簽下有個targetNamespace
,這裡指定了命名空間叫http://www.redwinter.com/schema/redwinter ,那麼在進行spring
配置文件的時候引入的namespace
就是這個,然後有個name="dl"
,這裡的這個dl
就是處理器中定義的元素名稱,而且必須一致,不然解析不到,下麵定義的屬性就是標簽屬性類中定義的刷新,這個id
是表示唯一的Bean
名稱。
編寫spring.schemas和spring.handlers文件
這裡直接列出完整文件內容:
spring.schemas
文件
http\://www.redwinter.com/schema/redwinter.xsd=META-INF/redwinter.xsd
這裡需要註意的是,這裡配置的key
也是需要在spring
配置文件中引入的,value
就是上一步定義的xsd
文件所在路徑
spring.handlers
文件
http\://www.redwinter.com/schema/redwinter=com.redwinter.test.RedwinterNameSpaceHandler
這裡配置的key
就是上一步定義的xsd
文件中定義的targetNamespace
,value
就是定義的命名空間處理器。
到這裡自定義標簽和解析就完成了,最後就需要在spring配置文件中引入並配置。
驗證自定義屬性標簽
我這裡定義個角spring-test.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:redwinter="http://www.redwinter.com/schema/redwinter"
xsi:schemaLocation="http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
http://www.redwinter.com/schema/redwinter http://www.redwinter.com/schema/redwinter.xsd
">
<!--自定義標簽-->
<redwinter:dl id ="redwinter" email="[email protected]" password="123456" username="冬玲記憶"/>
<redwinter:dl id ="redwinter123456" email="[email protected]" password="123456" username="冬玲記憶"/>
</beans>
驗證是否配置正確:
public class BeanCreate {
@Test
public void classPathXml() {
// ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-test.xml");
ClassPathXmlApplicationContext context = new MyClassPathXmlApplicationContext("classpath:spring-test.xml");
Redwinter redwinter = (Redwinter) context.getBean("redwinter");
System.out.println(redwinter.getEmail());
Redwinter redwinter123456 = (Redwinter) context.getBean("redwinter123456");
System.out.println(redwinter123456.getEmail());
}
}
輸出:
[email protected]
[email protected]
那說明自定義標簽生效了,並且成功解析出來。
接下來就是繼續介紹Spring
容器的實現AbstractApplicationContex#refresh
的第三個方法,這個方法其實就是BeanFactory
使用的前戲準備,而第一個方法是BeanFactory
刷新的前戲準備。