background
In Dubbo, XML can be used to configure related information and to import or export services. When the configuration is complete and the project is started, Spring reads the configuration file and generates the injected beans. How does Dubbo implement custom XML that Spring loads and reads?
Spring XML Schema extension mechanism. Starting with Spring 2.0, Spring began to provide an XML Schema-based extension mechanism for defining and configuring beans.
Spring XML Schema extension mechanism
Implementing the Spring XML Schema extension is as simple as completing the following four steps.
- Create an XML Schema file, which is called an XSD file because of its suffix name.
- Write one or more implementations
BeanDefinitionParser
。 - write
NamespaceHandler
The implementation class. - registered
NamespaceHandler
And XSD files.
We followed the above steps to complete Spring resolution of the following configuration.
<?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>
Copy the code
Create an XSD file
XSD file, mainly used to define XML format, used to verify XML validity. In the IDE, import the XSD file and edit the XML file to get hints.
Let’s generate an XSD file.
<?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>
Copy the code
In the XSD file above, www.test.com/demo is the custom namespace address, which will be used below.
Realize the BeanDefinitionParser
BeanDefinitionParser is implemented here, and the actual XML parsing is done here.
Because the above example is simple, we can simply inherit Spring provides an abstract class AbstractSingleBeanDefinitionParser, then implement relevant methods.
public class DemoBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
/** * returns the type that most needs to be injected into the Spring Bean *@param element
* @return* /
@Override
protectedClass<? > getBeanClass(Element element) {return DemoApplication.class;
}
/*** * This method does the actual parsing *@param element
* @param builder
*/
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String name=element.getAttribute("name");
builder.addPropertyValue("name",name); }}Copy the code
You could also implement BeanDefinitionParser directly, which would be more flexible, but it would be more complicated than this.
public class BeanApplicationDefinitionParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
String name=element.getAttribute("name");
// Bean definition, and finally produce Bean from this
RootBeanDefinition rootBeanDefinition=new RootBeanDefinition();
rootBeanDefinition.setBeanClass(DemoApplication.class);
rootBeanDefinition.setLazyInit(false);
// Add parsed attributes
rootBeanDefinition.getPropertyValues().add("name",name);
// Register the generated BeanDefinition. Missing this step will result in an error at the end of the generated Bean
parserContext.getRegistry().registerBeanDefinition("application",rootBeanDefinition);
returnrootBeanDefinition; }}Copy the code
Realize the NamespaceHandler
This step implements the NamespaceHandler. The developer custom NamespaceHandler simply inherits the NamespaceHandlerSupport abstract class and implements the init method. Register the previous implementation BeanDefinitionParser in this method.
public class DemoNameSpaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {/ / registerBeanDefinitionParser elementName for namespace ("application",new BeanApplicationDefinitionParser()); }}Copy the code
Register XSD and NamespaceHandler
In this step we need to generate two configuration files in meta-INF, spring.handlers and spring.schemas.
Spring.schemas Specifies the XSD file path.
http\://www.test.com/demo/demo.xsd=com/spring/learning/xml/schemas/autoring/leanrn/demo.xsd
Copy the code
Handlers specify the full class name of NamespaceHandler, including the preceding package name.
The caveat here is that you need to escape
A test run
First we produce the Spring XML configuration file.
<?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>
Copy the code
Note here that http://www.test.com/demo is defined in the XSD file.
Next we use SpringBoot, import the XML file, and run.
@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()); }}Copy the code
The output is:
Spring XML extension mechanism source research
Here we’ll focus on how custom XML extension files are loaded by Spring.
Spring is in the process of start reading beans from the BeanDefinitionDocumentReader label all configuration inside, This process will be through BeanDefinitionParserDelegate# parseCustomElement parsing custom elements.
The above parsing process yields a custom NamespaceHandler, which is then parsed by calling the Parse method.
Next we look at the NamespaceHandlerResolver#resolve method to see how to get a custom NamespaceHandler.
In this method, the NamespaceHandler is primarily fetched from the handlerMappings cache. The cache comes from the getHandlerMappings method, which will load our custom Spring. handlers file above.
After watching Spring load the NamespaceHandler, let’s take a look at how the most important BeanDefinitions are generated.
Spring uses NamespaceHandler.parse. Since we inherit NamespaceHandlerSupport, check out the implementation.
Getting a BeanDefinition registers it with the container, and the Bean is generated from the BeanDefinition. This process is not covered in this chapter, so you can search for it yourself.
Dubbo XML Schema extension implementation
Finally, let’s look at how the Dubbo XML Schema extension is implemented.
You can see that the Dubbo XML Schema extension corresponds exactly to the four Spring standard steps.
conclusion
Finally, summarize the whole text with a picture.
Help document
XML Schema extension mechanism in XSD-custom-Registration Spring