緣起 Dubbo ,講講 Spring XML Schema 擴展機制
背景
在 Dubbo 中,可以使用 XML 配置相關信息,也可以用來引入服務或者導出服務。配置完成,啟動工程,Spring 會讀取配置文件,生成注入 相關 Bean。那 Dubbo 如何實現自定義 XML 被 Spring 載入讀取?
Spring XML Schema 擴展機制。從 Spring 2.0 開始,Spring 開始提供了一種基於 XML Schema 格式擴展機制,用於定義和配置 bean。
Spring XML Schema 擴展機制
實現 Spring XML Schema 擴展,其實非常簡單,只需要完成下面四步。
- 創建 XML Schema 文件,由於該文件後綴名為 xsd,下面稱為 XSD 文件。
- 編寫實現一個或多個
BeanDefinitionParser
。 - 編寫
NamespaceHandler
實現類。 - 註冊
NamespaceHandler
以及 XSD 文件。
我們按照以上步驟,最終完整 Spring 解析如下配置。
<?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:demo="http://www.test.com/demo"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.test.com/demo http://www.test.com/demo/demo.xsd">
<demo:application name="test" id="test"/>
</beans>
創建 XSD 文件
XSD 文件,主要用來定義 XML 格式,用來驗證 XML 合法性。在 IDE 中,導入 XSD 文件,編輯 XML 文件可以獲得相關提示。
下面我們生成一個 XSD 文件。
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.test.com/demo"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.test.com/demo"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
<xsd:element name="application">
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="beans:identifiedType">
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
</xsd:schema>
上面 XSD 文件中 http://www.test.com/demo 為自定義命名空間地址,下面將會使用到。
實現 BeanDefinitionParser
這裡實現 BeanDefinitionParser,真正解析 XML 動作在這裡完成。
由於上面的例子比較簡單,我們可以直接繼承 Spring 提供的抽象類 AbstractSingleBeanDefinitionParser
,然後實現相關方法就可以了。
public class DemoBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
/**
* 返回最會需要注入 Spring Bean 的類型
* @param element
* @return
*/
@Override
protected Class<?> getBeanClass(Element element) {
return DemoApplication.class;
}
/***
* 這個方法完成真正解析動作
* @param element
* @param builder
*/
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String name=element.getAttribute("name");
builder.addPropertyValue("name",name);
}
}
當然也可以直接實現 BeanDefinitionParser,這樣更加靈活,但是這樣相比於上面這個就比較複雜了。
public class BeanApplicationDefinitionParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
String name=element.getAttribute("name");
// Bean 定義,最後根據這個生產 Bean
RootBeanDefinition rootBeanDefinition=new RootBeanDefinition();
rootBeanDefinition.setBeanClass(DemoApplication.class);
rootBeanDefinition.setLazyInit(false);
// 添加解析的屬性
rootBeanDefinition.getPropertyValues().add("name",name);
// 將生成的 BeanDefinition 註冊,少了這一步將會導致最後生成 Bean 時報錯
parserContext.getRegistry().registerBeanDefinition("application",rootBeanDefinition);
return rootBeanDefinition;
}
}
實現 NamespaceHandler
這一步實現 NamespaceHandler,開發者自定義 NamespaceHandler 只要繼承 NamespaceHandlerSupport
抽象類,實現 init
方法。在這個方法中註冊上面一步實現 BeanDefinitionParser
。
public class DemoNameSpaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// elementName 為命名空間
registerBeanDefinitionParser("application",new BeanApplicationDefinitionParser());
}
}
註冊 XSD 以及 NamespaceHandler
這一步我們需要在 META-INF 中生成兩個配置文件,分別為 spring.handlers
,spring.schemas
。
spring.schemas
指定 XSD 文件路徑。
http://www.test.com/demo/demo.xsd=com/spring/learning/xml/schemas/autoring/leanrn/demo.xsd
spring.handlers
指定 NamespaceHandler
完整類名,既包含前面的包名。
這裡需要注意的是
:
需要進行轉義
測試運行
首先我們生產 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:demo="http://www.test.com/demo"
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.test.com/demo http://www.test.com/demo/demo.xsd">
<demo:application name="test" id="test"/>
</beans>
這裡需要注意需要使用 XSD 文件中定義 http://www.test.com/demo
。
接著我們使用 SpringBoot ,導入 XML 文件,然後運行。
@SpringBootApplication
@ImportResource(locations = {"classpath:applicationContext.xml"})
public class XmlSchemaApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(XmlSchemaApplication.class, args);
DemoApplication demoApplication=applicationContext.getBean(DemoApplication.class);
System.out.println("application name is "+demoApplication.getName());
}
}
輸出結果為: