Analysis of custom tags and registration of Beans in Spring source code analysis

Earlier we degraded the analysis of default tags in Spring. Below we introduce the analysis of custom tags for Beans. Why Spring is so powerful is that it has strong scalability. Therefore, our custom tags can also be parsed by Spring, such as Dubbo. The analysis of the custom tag is also delegated to BeanDefinitionParserDelegate. The method called is: parseCustomElement(Element ele), the source code is as follows:

public BeanDefinition parseCustomElement(Element ele) {
    return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    //获取命名空间
    String namespaceUri = getNamespaceURI(ele);
    //根据命名空间获取一个
    NamespaceHandler handler =  this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
        return null;
    }
    //通过NamespaceHandler解析标签
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

The parsing of custom tags through the above code is accomplished through a NamespaceHandler instance. First, let’s look at the definition of NamespaceHandler as follows:

public interface NamespaceHandler {
    //初始化
    void init();
    //解析元素返回一个BeanDefinition
    BeanDefinition parse(Element element, ParserContext parserContext);
    //解析元素返回一个BeanDefinitionHolder 
    BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);

The NamespaceHandler parses the label and returns the label to a Beandefinition instance or BeanDefinitionHolder instance. The following is the implementation of the NamespaceHandler for custom label resolution provided by Spring :

The acquisition of NamespaceHandler is done by DefaultNamespaceHandlerResolver, which is similar to the SPI mechanism. It loads all Handlers from META-INF/spring.handlers , instantiates them, and caches them in Map. The following is the configuration in spring-beans

http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

 If we want to implement our own NamespaceHandler, we only need to inherit NamespaceHandlerSupport, which is simpler and we only need to implement the init method, and initialize our custom label parser in the init method. The ContextNamespaceHandler provided by Spring handles the context namespace as follows:

public class ContextNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
	registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
	registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
	registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
	registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
	registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }
}

We will not go into details here. Those who are interested can read the source code by themselves. In order to deepen the understanding of custom label analysis, let's write an example of parsing custom labels. Here we first introduce the use of the tag parser BeanDefinitionParser, which is used to parse tags in the Spring configuration. If you want to parse custom tags, we need to implement some methods according to the situation. The following are some implementations of BeanDefinitionParser, we only need to inherit according to our own needs Just one method:

First of all, we need to define the XML specification file, that is, the xsd file. If you don’t know about xsd, you can Baidu yourself. I am also dissatisfied with a bottle and half a bottle of sloshing, so I directly copy the definition of Spring official documents. The definition is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.baidu.com/schema/myns"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:beans="http://www.springframework.org/schema/beans"
    targetNamespace="http://www.baidu.com/schema/myns"
    elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xsd:import namespace="http://www.springframework.org/schema/beans"/>
    <xsd:element name="dateformat">
        <xsd:complexType>
	    <xsd:complexContent>
	        <xsd:extension base="beans:identifiedType">
		    <xsd:attribute name="lenient" type="xsd:boolean" />
		    <xsd:attribute name="pattern" type="xsd:string" use="required" />
		</xsd:extension>
	    </xsd:complexContent>
	</xsd:complexType>
    </xsd:element>
</xsd:schema>

Then you need to configure the xsd file written above in the spring.schemas file in the META-INF directory. An example is as follows:

http\://www.baidu.com/schema/myns/myns.xsd=cn/org/microservice/spring/ioc/customized/myns.xsd

Then you need to write a BeanDefinitionParse tag parser. Here we inherit the code of the AbstractSingleBeanDefinitionParser class as follows:

public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    //返回标签要解析成的Bean的Class实例
    protected Class getBeanClass(Element element) {
        return SimpleDateFormat.class;
    }
    //解析元素
    protected void doParse(Element element, BeanDefinitionBuilder bean) {
        // this will never be null since the schema explicitly requires that a
        // value be supplied
        String pattern = element.getAttribute("pattern");
        bean.addConstructorArg(pattern);
        // this however is an optional property
        String lenient = element.getAttribute("lenient");
        if (StringUtils.hasText(lenient)) {
            bean.addPropertyValue("lenient", Boolean.valueOf(lenient));
        }
    }
}

After that, we need to register the custom BeanDefinitionParse we wrote earlier in Spring, so we need to create a custom NamespaceHandler , the code is as follows:

public class MyNamespaceHandler extends NamespaceHandlerSupport {
    public void init() {
        registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());
    }
}

After writing, you need to configure MyNamespaceHandler to the spring.handlers file in the META-INF directory. The configuration is as follows:

http\://www.baidu.com/schema/myns=cn.org.microservice.spring.ioc.customized.MyNamespaceHandler

So far, our custom label parsing work has been completed. Below we use the xml configuration and test the custom label written in the previous section as follows:

<?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:myns="http://www.baidu.com/schema/myns"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.baidu.com/schema/myns 
http://www.baidu.com/schema/myns/myns.xsd">
    <myns:dateformat id="defaultDateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true" />
</beans>
public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
    System.out.println(context.getBean("defaultDateFormat"));
}

The SimpleDateFormat instance will be printed in the above code. The analysis of Spring's custom label is easy to understand, mainly the writing of xsd files and the writing of custom label parser. Many third-party frameworks integrated with Spring have implementations of custom tag resolution, such as Dubbo.

Guess you like

Origin blog.csdn.net/wk19920726/article/details/108828451