An overview of the

In the Spring, from the beginning of the AbstractXmlApplicationContext, through to the NamespaceHandler & BeanDefinitionParser, to implement a custom XML configuration function.

1 XML load parsing

XML file to load from AbstractXmlApplicationContext. LoadBeanDefinitions (DefaultListableBeanFactory) begin to implement:

        @Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// then proceed with actually loading the bean definitions.
		initBeanDefinitionReader(beanDefinitionReader);
		// Load the bean definition
		loadBeanDefinitions(beanDefinitionReader);
	}
Copy the code

Loading process is implemented by XmlBeanDefinitionReader XmlBeanDefinitionReader inherited AbstractBeanDefinitionReader.

Spring environment load, you need to call AbstractApplicationContext. Refresh (), from the refresh (), the complete process is as follows:

    1. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    1. AbstractRefreshableApplicationContext.refreshBeanFactory();
    1. AbstractXmlApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory);
    1. AbstractBeanDefinitionReader.loadBeanDefinitions(Resource… resources);
    1. XmlBeanDefinitionReader.loadBeanDefinitions(Resource);
    1. XmlBeanDefinitionReader.doLoadBeanDefinitions;
    1. XmlBeanDefinitionReader.registerBeanDefinitions;
    1. DefaultBeanDefinitionDocumentReader.registerBeanDefinitions;
    1. DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions;
    1. DefaultBeanDefinitionDocumentReader parseBeanDefinitions.

DefaultBeanDefinitionDocumentReader. ParseBeanDefinitions implementation is as follows:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
					    // If it is the default namespace, execute parseDefaultElement to load the default XML elements, such as import, alias, bean, etc.
						parseDefaultElement(ele, delegate);
					}
					else {
					    / / otherwise perform BeanDefinitionParserDelegate. ParseCustomElement (root) load custom XML elements.delegate.parseCustomElement(ele); }}}}else{ delegate.parseCustomElement(root); }}Copy the code

Is getting the custom BeanDefinitionParserDelegate. ParseCustomElement namespace, and according to the namespace NamespaceHandler, Namespacehandler. parse is then executed and the BeanDefinition is returned.

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
                / / get the namespace
		String namespaceUri = getNamespaceURI(ele);
		// Get the corresponding NamespaceHandler
		NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
		if (handler == null) {
			error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
			return null;
		}
		/ / return BeanDefinition
		return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
	}
Copy the code

The specific process is as follows:

  • 1. This. ReaderContext. GetNamespaceHandlerResolver () :

      1. Return NamespaceHandlerResolver, implementation class is DefaultNamespaceHandlerResolver;
      1. String defined DefaultNamespaceHandlerResolver DEFAULT_HANDLER_MAPPINGS_LOCATION = “the meta-inf/spring. Handlers”;
  • 2. DefaultNamespaceHandlerResolver. Resolve:

      1. Perform the NamespaceHandler init
      1. Based on the spring.handlers content and the current namespace, the custom NamespaceHandler implementation is returned.

2 NamespaceHander & BeanDefinitionParser

  • Org. Springframework. Beans. Factory. XML. BeanDefinitionParser: parse the XML and returns BeanDefinition object.
  • Org. Springframework. Beans. Factory. XML. NamespaceHander: will implement the BeanDefinitionParser object to register to the specified XML elements.

So, generally inherit NamespaceHandlerSupport or AbstractSingleBeanDefinitionParser can directly.

3 Complete implementation steps

3.1 Defining a Bean

public class ServerConfig {
    private String host;
    private int port;
    public String getHost(a) {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public int getPort(a) {
        return port;
    }
    public void setPort(int port) {
        this.port = port; }}Copy the code

3.2 Creating an XSD File

Create an XSD and place it in meta-INF. For example, the file name is custom.xsd.


      
<xsd:schema
        xmlns="custom"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:beans="http://www.springframework.org/schema/beans"
        targetNamespace="custom"
        >
    <xsd:import namespace="http://www.springframework.org/schema/beans" />
    <xsd:element name="serverConfig">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="beans:identifiedType">
                    <xsd:attribute name="host" type="xsd:string" />
                    <xsd:attribute name="port" type="xsd:int" />
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>
</xsd:schema> 
Copy the code

3.3 Implement the BeanDefinitionParser interface

Inherit AbstractSingleBeanDefinitionParser, pay equal attention to write getBeanClass and doParse two methods, analytical custom. XSD defined in the XML node attributes.

public class ServerConfigBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    @Override
    protectedClass<? > getBeanClass(Element element) {return ServerConfig.class;
    }
    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        String name = element.getAttribute("host");
        String port = element.getAttribute("port");

        if (StringUtils.hasText(name)) {
            builder.addPropertyValue("host", name);
        }
        if (StringUtils.hasText(port)) {
            builder.addPropertyValue("port", Integer.valueOf(port)); }}}Copy the code

3.4 Implement the NameSpaceHander interface

Inherit NamespaceHandlerSupport, rewrite the init () method is: will the custom. XSD defined in the XML root node, the registration for the above implementation ServerConfigBeanDefinitionParser object.

public class ServerConfigNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init(a) {
        registerBeanDefinitionParser("serverConfig".newServerConfigBeanDefinitionParser()); }}Copy the code

3.5 Specifying an XSD file

Classpath, create the meta-INF /spring.schemas file and write the following contents:

custom.xsd=classpath:META-INF/custom.xsd
Copy the code

3.6 Specifying the implementation class of NameSpaceHander

Create a meta-INF /spring.hander file in the classpath and write the following:

custom=xxx.xxx.ServerConfigNamespaceHandler
Copy the code

3.7 XSD introduced in Spring

In Spring’s XML configuration, introduce the XSD declared above:


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tinyrpc="custom"
       xsi:schemaLocation="Http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd the custom META-INF/custom.xsd"
       default-lazy-init="false" default-autowire="byName">
       <custom:serverConfig id="TestServer" host="Localhost" port="8888"></custom:serverConfig>
</beans>
Copy the code

You can declare a Bean with the id testServer.

4 summarizes

In summary, to implement basic custom XML, follow the following steps:

  • 1. Define the Bean;
  • 2. Create the XSD;
  • 3. Emphasis on inheriting AbstractSingleBeanDefinitionParser write relevant methods;
  • 4. Inherit NamespaceHandlerSupport and rewrite related methods;
  • 5. Use meta-INF /spring.schemas to specify XSD files.
  • 6. Use meta-INF /spring.hander to specify NamespaceHander.
  • 7 Introduce declared XSD in Spring’s XML configuration.

Aop configuration in Spring, transaction configuration, etc., and custom XML configuration in Ali Dubbo, Meituan Pigeon are all implemented in this way.

For details about the Spring loading process, see the Spring loading Process and core classes.