ApplicationListener, ApplicationContextAware, BeanFactoryAware This article covers the extended use of NamespaceHandler.

I believe many of you are familiar with these classes, and you can use RPC frameworks based on Java, such as Dubbo, SOFARpc, etc. This article will start with a few small demos to understand the basic concepts and programming flow, and then analyze how it is used in SOFARpc.

NamespaceHandler

NamespaceHandler is a spring-provided NamespaceHandler. In the figure below, everything is provided by Spring itself, except for the BridgeNameSpaceHandler that was jumped-in in this demo.

bean
context
AopNamespaceHandler

When we use an XML-based Spring configuration, we may need to configure a tag such as < AOP :config />. Before configuring this tag, we usually need to import the namespace in which the AOP resides:

<?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:aop="http://www.springframework.org/schema/aop"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" />
Copy the code

A few words about AOP: representation and basic concepts will not be explained here, but let’s write a custom XML that follows the official documentation process. The end result is as follows:

<bridge:application id="bridgeTestApplication"
                    name="bridgeTestApplication"  
                    version="1.0" 
                    organization="bridge.glmapper.com"
                    owner="leishu@glmapper"/>
Copy the code

1. Define XSD files

The syntax rules for XSD files are outside the scope of this article. The following file is simple, defining an Element name as Application, corresponding to the application in Bridge: Application. Attribute is the name of the corresponding attribute shown above.

<?xml version="1.0" encoding="UTF-8" standalone="no"? >
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            xmlns:tool="http://www.springframework.org/schema/tool"
            xmlns="http://bridge.glmapper.com/schema/bridge"
            targetNamespace="http://bridge.glmapper.com/schema/bridge">

    <xsd:import namespace="http://www.springframework.org/schema/beans"/>

    <xsd:complexType name="applicationType">
        <xsd:attribute name="id" type="xsd:ID"/>
        <xsd:attribute name="name" type="xsd:string" use="required"/>
        <xsd:attribute name="version" type="xsd:string"/>
        <xsd:attribute name="owner" type="xsd:string"/>
        <xsd:attribute name="organization" type="xsd:string"/>
    </xsd:complexType>

    <xsd:element name="application" type="applicationType"/>
</xsd:schema>
Copy the code

2. Write NamespaceHandler

In addition to the schema, we need a NamespaceHandler that will parse all elements of this specific namespace Spring encounters while parsing configuration files.

The NamespaceHandler is written to parse the configuration file.

Specifically, NamespaceHandler finds a BeanDefinitionParser based on the schema and node name, and BeanDefinitionParser does the parsing.

Spring provides a default implementation class NamespaceHandlerSupport and AbstractSingleBeanDefinitionParser, the easiest way is to inherit these two classes.

This is done by inheriting the NamespaceHandlerSupport abstract class.

public class BridgeNamespaceHandler extends NamespaceHandlerSupport {
    public void init(a) {
        registerBeanDefinitionParser("application".newApplicationBeanDefinitionParser()); }}Copy the code

Here you’re really just registering a parser; the specific BeanDefinitionParser is what maps XML elements to specific beans.

3. Write BeanDefinitionParser

Here we define our BeanDefinitionParser implementation class directly by implementing the BeanDefinitionParser interface. Use in SPFARpc about AbstractSingleBeanDefinitionParser will be involved.

public class ApplicationBeanDefinitionParser implements BeanDefinitionParser {

    public BeanDefinition parse(Element element, ParserContext parserContext) {
        //beanDefinition
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(ApplicationConfig.class);
        beanDefinition.setLazyInit(false);
        //解析id
        String id = element.getAttribute("id");
        beanDefinition.getPropertyValues().add("id", id);
        //解析name
        beanDefinition.getPropertyValues().add("name",
        element.getAttribute("name"));
        / / resolution version
        beanDefinition.getPropertyValues().add("version",
        element.getAttribute("version"));
        //owner
        beanDefinition.getPropertyValues().add("owner",
        element.getAttribute("owner"));
        //organization
        beanDefinition.getPropertyValues().add("organization",
        element.getAttribute("organization"));
    
        parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
        returnbeanDefinition; }}Copy the code

Here we need to understand is began to parse the custom tag is through BeanDefinitionParserDelegate – > parseCustomElement method to deal with, as shown in the figure below:

Get the current namespaceUri, the namespace defined in the XSD, from the ele element, Then delegate to DefaultNamespaceResolver to get the specific handler (BridgenamspaceHandler) and parse.

Handlers and Spring. schmas

http\://bridge.glmapper.com/schema/bridge=
com.glmapper.extention.namespacehandler.BridgeNamespaceHandler

http\://bridge.glmapper.com/schema/bridge.xsd=META-INF/bridge.xsd
Copy the code

To do this, we need to put our NamespaceHandler and XSD files in the Spring. handlers and spring.schmas files in the meta-INF directory. This allows us to use our custom tags in the Spring configuration file. As follows:

<?xml version="1.0" encoding="UTF-8"? >
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:bridge="http://bridge.glmapper.com/schema/bridge"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://bridge.glmapper.com/schema/bridge http://bridge.glmapper.com/schema/bridge.xsd">
       
    <bridge:application id="bridgeTestApplication"
                    name="bridgeTestApplication"  
                    version="1.0" 
                    organization="bridge.glmapper.com"
                    owner="leishu@glmapper"/>
</beans>
Copy the code

Verify to get our bean from the container:

public static void main(String[] args) {
    ApplicationContext applicationContext = new
    ClassPathXmlApplicationContext("classpath:bean.xml");
    
    ApplicationConfig applicationConfig = (ApplicationConfig)
    applicationContext.getBean("bridgeTestApplication");
    
    System.out.println("applicationConfig = "+applicationConfig);
}
Copy the code

Example output:

applicationConfig = ApplicationConfig {
    id=bridgeTestApplication, 
    name='bridgeTestApplication', 
    version='1.0', 
    owner='leishu@glmapper', 
    organization='bridge.glmapper.com'
}
Copy the code

Overall, if we want to implement our own XML tags, we only need to do the following:

  • 1. Define XSD files
  • 2. Write NamespaceHandler
  • 3. Write BeanDefinitionParser
  • Handlers and Spring. schmas

Use analysis in SOFARpc

The RPC. XSD file in SOFARpc is integrated in sofaboot. XSD file, as shown in sofA-boot

The XSD file is not pasted here, it’s a bit long

Spring. Handlers and spring. Schmas

First look at the Spring. handlers and Spring. schmas configuration:

http\://sofastack.io/schema/sofaboot=
com.alipay.sofa.infra.config.spring.namespace.handler.SofaBootNamespaceHandler

http\://sofastack.io/schema/sofaboot.xsd=
META-INF/com/alipay/sofa/infra/config/spring/namespace/schema/sofaboot.xsd

http\://sofastack.io/schema/rpc.xsd=
META-INF/com/alipay/sofa/infra/config/spring/namespace/schema/rpc.xsd

Copy the code

Handlers find NamespaceHandler from Spring. handlers: SofaBootNamespaceHandler.

SofaBootNamespaceHandler

There is no BeanDefinitionParser like above. This is a neat design, loading specific BeanDefinitionParser via SPI.

public class SofaBootNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init(a) {
        ServiceLoader<SofaBootTagNameSupport> serviceLoaderSofaBoot =
        ServiceLoader.load(SofaBootTagNameSupport.class);
        //SOFABoot
        for (SofaBootTagNameSupport tagNameSupport : serviceLoaderSofaBoot) {
            this.registerTagParser(tagNameSupport); }}private void registerTagParser(SofaBootTagNameSupport tagNameSupport) {
        if(! (tagNameSupportinstanceof BeanDefinitionParser)) {
            // log
            return; } String tagName = tagNameSupport.supportTagName(); registerBeanDefinitionParser(tagName, (BeanDefinitionParser) tagNameSupport); }}Copy the code

Here you can see there are ReferenceDefinitionParser and ServiceDefinitionParser two class, corresponding to the service reference and service exposure.

Below ReferenceDefinitionParser, for example, look at it first class diagram:

Analytical work is done in AbstractContractDefinitionParser class, ReferenceDefinitionParser yourself just do some special processing service 】 【 JVM – first, JVM.

summary

This article has learned how to write our custom XML tags through NamespaceHandler. From the perspective of NamespaceHandler, we can have a good understanding of the most basic XML-based service references and exposed implementation ideas in some RPC frameworks. In addition, by analyzing SOFARpc, you also learned about the extended use of NamespaceHandler in real engineering components.

Glmapper-spring-extention