Preface
In the previous section Spring Decryption - Analysis of Default Tags, we focus on analyzing how Spring parses the default tags. So this chapter continues to explain label parsing, focusing on how to parse custom tags. Without further ado, let’s take a look at the detailed introduction.
Custom Tags
Before explaining custom tag analysis, please see how to customize tags
Define XSD file
Define an XSD file to describe component content
<?xml version="1.0" encoding="UTF-8"?><xsd:schema xmlns="http://www.battcn.com/schema/battcn" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" targetNamespace="http://www.battcn.com/schema/battcn" 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>
Define parsing rules
1. Create a class to implement the BeanDefinitionParser interface (can also inherit the classes provided by Spring) to parse the definitions and component definitions in the XSD file.
public class ApplicationBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { // The type of the received object is such as: String name = (String) context.getBean("battcn"); return String.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder bean) { // The name attribute defined in xsd String name = element.getAttribute("name"); bean.addConstructorArgValue(name); }}Here is a ApplicationBeanDefinitionParser that inherits AbstractSingleBeanDefinitionParser (is a subclass of BeanDefinitionParser). The focus is to override doParse, parse XML tags in it, and then inject the parsed value (Levin) into the constructor.
2. Create a class to inherit NamespaceHandlerSupport abstract class
public class BattcnNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("application", new ApplicationBeanDefinitionParser()); }} The function of BattcnNamespaceHandler is very simple, which is to tell the Spring container that the tag <battcn:application /> should be parsed by that parser (here we customized: ApplicationBeanDefinitionParser), which is responsible for registering components to the Spring container.
3. Write spring.handlers and spring.schemas files
The directory where the file is stored is located in resources/META-INF/file name
spring.handlers
http/://www.battcn.com/schema/battcn=com.battcn.handler.BattcnNamespaceHandler
spring.schemas
http/://www.battcn.com/schema/battcn.xsd=battcn.xsd
4. Use custom tags
Declare the bean.xml file, defined 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:battcn="http://www.battcn.com/schema/battcn" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.battcn.com/schema/battcn http://www.battcn.com/schema/battcn.xsd"> <battcn:application id="battcn" name="Levin"/></beans>
Create a test class. If you see the console output Levin word, it means that the custom label is normal.
public class Application { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); String name = (String) context.getBean("battcn"); System.out.println(name); }}5. As shown in the figure
Source code analysis
Custom tag resolution portal
public class BeanDefinitionParserDelegate { @Nullable public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containsBd) { // Get the namespace address http://www.battcn.com/schema/battcn String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } // NamespaceHandler is the application registered in the customized BattcnNamespaceHandler 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 handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }} Like the default tag resolution rules, the namespace is obtained through getNamespaceURI(Node node) . So where is this.readerContext.getNamespaceHandlerResolver() obtained? We track the code and we can find that when the project starts, all the META-INF/spring.handles file contents will be parsed in the XmlBeanDefinitionReader and stored in handlerMappers (a ConcurrentHashMap). When resolve(namespaceUri) is called to check, the cached content will be extracted for comparison.
public class XmlBeanDefinitionReader { public NamespaceHandlerResolver getNamespaceHandlerResolver() { if (this.namespaceHandlerResolver == null) { this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver(); } return this.namespaceHandlerResolver; }}resolve
1. Load the specified NamespaceHandler map, and the extracted NamespaceHandler is cached, and then return
public class DefaultNamespaceHandlerResolver { @Override @Nullable public NamespaceHandler resolve(String namespaceUri) { Map<String, Object> handlerMappings = getHandlerMappings(); // Extract handlerOrClassName from handlerMappings Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } else { String className = (String) handlerOrClassName; try { Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } // Find the corresponding information based on the namespace NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); // Handler Initialize namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", ex); } catch (LinkageError err) { throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", err); } } }}Tag analysis
After loading the NamespaceHandler, the BattcnNamespaceHandler has been initialized, and the BattcnNamespaceHandler also calls init() method to complete the initialization work. Therefore, I will continue to execute this code: handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); specific tag solution.
public class NamespaceHandlerSupport { @Override @Nullable public BeanDefinition parse(Element element, ParserContext parserContext) { BeanDefinitionParser parser = findParserForElement(element, parserContext); return (parser != null ? parser.parse(element, parserContext) : null); } @Nullable private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { // parse the application String localName in <battcn:application /> .getDelegate().getLocalName(element); BeanDefinitionParser parser = this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal( "Cannot locale BeanDefinitionParser for element [" + localName + "]", element); } return parser; }}Simply put, it is to find the ApplicationBeanDefinitionParser instance from parsers and call its own doParse method for further analysis. Finally, it's the same routine of parsing the default tag...
Summarize
After a few unknown autumn, winter, spring and summer, everything will follow the direction you want...
Okay, the above is the entire content of this article. I hope that the content of this article has certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support to Wulin.com.
Say something
Full text code: https://gitee.com/battcn/battcn-spring-source/tree/master/Chapter2