In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article is to share with you about how to use Spring custom namespace, the editor thinks it is very practical, so I share it with you to learn. I hope you can get something after reading this article.
When Spring parses tags in a xml file, it distinguishes whether the current tag is four basic tags (import, alias, bean, and beans) or a custom tag. If it is a custom tag, it parses the current tag according to the logic of the custom tag. In addition, even bean tags can use custom attributes or custom child tags. This article will explain the use of custom tags and custom attributes, and explain the implementation of custom tags and custom attributes from the point of view of source code.
The Spring framework, starting with version 2. 0, provides an extension mechanism for defining bean based on the Schema-style Spring XML format. Schema-based XML was introduced to simplify the XML configuration of Traditional. Through the definition of Schema, some configuration forms that need to be defined by several bean definitions or a combination of complex bean are presented in another simple and readable configuration form.
The Schema-based XML consists of three parts, and we are illustrated by a picture:
Namespace-has a very clear logical classification
Element-- has very clear process semantics
Attributes-with very simple configuration options
For example, the meaning of this configuration is to implement the configuration of Annotation drivers in the space of mvc. Among them, mvc represents the valid scope of the configuration, annotation-driven expresses a dynamic process, the actual logical meaning is: the implementation of the whole SpringMVC is based on the Annotation pattern, please register the relevant behavior patterns for me.
Here's how to write bean definition parsing for custom XML and integrate this parsing into the Spring IOC container. We will mention an important concept in later content, that is, bean definition. In fact, there is an important concept in Spring that is bean. The object BeanDefinition is the object parsed by the corresponding tag.
Use the following simple steps to create a new xml configuration extension:
Authoring A XML schema is used to describe your custom element (s)
A custom NamespaceHandler implementation of Coding (this is a very simple step, don't worry)
Coding one or more BeanDefinitionParse implementations (this is the most important)
Registeringr registers the above to Spring (this is also a simple answer step)
The above steps will be described in turn below. For example, we need to create a XML extension (custom xml element) that allows us to configure the SimpleDateFormat object (in the java.text package) in an easy way. Finally, we can define a bean definition of type SimpleDateFormat as follows:
1 、 Authoring the schema
To create a XML configuration extension for the Spring IOC container, we need to write a XML Schema to describe the extension. The following schema can be used to configure the SimpleDateFormat object
The above schema will be used to configure the SimpleDateFormat object. Use element. Context file directly in a xml application.
Note: the above XML fragment essentially has the same meaning as the following XML fragment.
2 、 Coding a NamespaceHandler
For the schema above, we need a NamespaceHandler to parse all the elements in this particular namespace configuration file that Spring encounters. This NamespaceHandler will be concerned with parsing the myns:dateformat element.
This NamespaceHandler interface is relatively simple and includes three important methods.
Init (). NamespaceHandler is instantiated before spring uses handler
BeanDefinition parse (Element, ParseContext)-when Spring encounters the top-level element defined above (that is, myms) will be called, this method can register bean definitions and can return a bean definition.
BeanDefinitionHolder decorate (Node, BeanDefinitionHolder, ParserContext), which is called when spring encounters an attribute or is embedded in an element in namespace.
In many cases, the xml element of each top-level in the spring xml configuration file represents a bean definition of type single (in our case, the element of a single represents the SimpleDateFormat bean definition of a single). Spring provides a number of convenient classes to support this scenario. In this example, we will use NamespaceHandlerSupport.
Package org.springframework.samples.xml; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class MyNamespaceHandler extends NamespaceHandlerSupport {public void init () {registerBeanDefinitionParser ("dateformat", new SimpleDateFormatBeanDefinitionParser ());}}
Let's talk about why we inherit the abstract class NamespaceHandlerSupport.
First take a look at the Spring custom namespace loading process:
XmlBeanDefinitionReader entrance
DefaultDocumentLoader loads and parses a XML file into a Document instance to get EntityResolver and ErrorHandler from BeanDefinitionReader.
Create a BeanDefinitionDocumentReader in XmlBeanDefinitionReader, and iterate through each Element in Document in this BeanDefinitionDocumentReader.
For each Element, if it is the default URI (that is, defined in the beans namespace), call the parseDefaultElement () method, otherwise call the parseCustomElement () method in BeanDefinitionParserDelegate.
In the parseCustomElement () method, it finds the namespaceURI of the current Element, then gets the custom NamespaceHandler from NamespaceHandlerResolver, and uses this NamespaceHandler to parse the Element. Since we have registered the corresponding BeanDefinitionParser of different element name in the init () method, we can use this custom BeanDefinitionParser to parse the custom Element.
The default NamespaceHandlerResolver (DefaultNamespaceHandlerResolver) will find all the META-INF/spring.handlers files under the current classpath, load them, read the contents into namespaceURI to NamespaceHandler's map, and initialize all NamespaceHandler.
Take a look at the parseCustomElement () method in BeanDefinitionParserDelegate:
Public BeanDefinition parseCustomElement (Element ele) {return this.parseCustomElement (ele, (BeanDefinition) null);} public BeanDefinition parseCustomElement (Element ele, BeanDefinition containingBd) {String namespaceUri = this.getNamespaceURI (ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver (). Resolve (namespaceUri); if (handler = = null) {this.error ("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null } else {return handler.parse (ele, new ParserContext (this.readerContext, this, containingBd);}}
Where the parse method is the method in the abstract class NamespaceHandlerSupport:
Public BeanDefinition parse (Element element, ParserContext parserContext) {return this.findParserForElement (element, parserContext) .parse (element, parserContext);} private BeanDefinitionParser findParserForElement (Element element, ParserContext parserContext) {String localName = parserContext.getDelegate () .getLocalName (element); BeanDefinitionParser parser = (BeanDefinitionParser) this.parsers.get (localName); if (parser = = null) {parserContext.getReaderContext () .fatal ("Cannot locate BeanDefinitionParser for element [" + localName + "]", element) } return parser;}
And some of them
BeanDefinitionParser parser = (BeanDefinitionParser) this.parsers.get (localName); the parsers in this sentence is a HashMap that overrides the init () method in the abstract class inheriting NamespaceHandlerSupport:
Public void init () {registerBeanDefinitionParser ("dateformat", new SimpleDateFormatBeanDefinitionParser ();}
The registerBeanDefinitionParser method of the class NamespaceHandlerSupport:
Private final Map parsers = new HashMap (); protected final void registerBeanDefinitionParser (String elementName, BeanDefinitionParser parser) {this.parsers.put (elementName, parser);}
The parse method in return this.findParserForElement (element, parserContext) .parse (element, parserContext); is the method in the abstract class AbstractBeanDefinitionParser:
Public final BeanDefinition parse (Element element, ParserContext parserContext) {AbstractBeanDefinition definition = this.parseInternal (element, parserContext); if (definition! = null & &! parserContext.isNested ()) {try {String id = this.resolveId (element, definition, parserContext) If (! StringUtils.hasText (id)) {parserContext.getReaderContext () .error ("Id is required for element'" + parserContext.getDelegate () .getLocalName (element) + "'when used as a top-level tag", element);} String [] aliases = null If (this.shouldParseNameAsAliases ()) {String name = element.getAttribute ("name"); if (StringUtils.hasLength (name)) {aliases = StringUtils.trimArrayElements (StringUtils.commaDelimitedListToStringArray (name));} BeanDefinitionHolder holder = new BeanDefinitionHolder (definition, id, aliases) This.registerBeanDefinition (holder, parserContext.getRegistry ()); if (this.shouldFireEvents ()) {BeanComponentDefinition componentDefinition = new BeanComponentDefinition (holder); this.postProcessComponentDefinition (componentDefinition); parserContext.registerComponent (componentDefinition) }} catch (BeanDefinitionStoreException var8) {parserContext.getReaderContext () .error (var8.getMessage (), element); return null;}} return definition;}
This.registerBeanDefinition (holder, parserContext.getRegistry ()); finally, the registerBeanDefinition method of BeanDefinitionReaderUtils is called to register the parsed Bean,BeanDefinitionReaderUtils with the IoC container. The source code is as follows:
/ / register the resolved BeanDefinitionHold with the container public static void registerBeanDefinition (BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {/ / get the name of the resolved BeanDefinition String beanName = definitionHolder.getBeanName (); / / register the BeanDefinition registry.registerBeanDefinition (beanName, definitionHolder.getBeanDefinition ()) with the IoC container; / / if the resolved BeanDefinition has an alias, register the alias String [] aliases = definitionHolder.getAliases () with the container If (aliases! = null) {for (String aliase: aliases) {registry.registerAlias (beanName, aliase);}
When calling BeanDefinitionReaderUtils to register the parsed BeanDefinition with the IoC container, it is DefaultListableBeanFactory that really completes the registration function.
DefaultListableBeanFactory registers the resolved BeanDefinition with the IoC container:
In DefaultListableBeanFactory, a collection object of HashMap is used to store the parsed BeanDefinition registered in the IoC container. The main source codes for registering with the IoC container are as follows:
/ / Storage Russian BeanDefinition private final Map beanDefinitionMap = new ConcurrentHashMap (); / / register parsed BeanDefiniton public void registerBeanDefinition (String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {Assert.hasText (beanName, "Bean name must not be empty") with the IoC container; Assert.notNull (beanDefinition, "BeanDefinition must not be null") / / verify the parsed BeanDefiniton if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition) .validate ();} catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException (beanDefinition.getResourceDescription (), beanName, "Validation of bean definition failed", ex) }} / / Thread synchronization is required during registration to ensure data consistency synchronized (this.beanDefinitionMap) {Object oldBeanDefinition = this.beanDefinitionMap.get (beanName) / / check whether a BeanDefinition with the same name has been registered in the IoC container. If so, / / and overwriting the registered Bean is not allowed. Then a registration failure exception if (oldBeanDefinition! = null) {if (! this.allowBeanDefinitionOverriding) {throw new BeanDefinitionStoreException (beanDefinition.getResourceDescription (), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean'" + beanName + "': There is already [" + oldBeanDefinition + "] bound.") } else {/ / if overwriting is allowed, the Bean with the same name Later registered override first registered if (this.logger.isInfoEnabled ()) {this.logger.info ("Overriding bean definition for bean'" + beanName + "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]") } / / No Bean with the same name has been registered in the IoC container. Follow the normal registration process to register else {this.beanDefinitionNames.add (beanName); this.frozenBeanDefinitionNames = null;} this.beanDefinitionMap.put (beanName, beanDefinition) / / reset the cache resetBeanDefinition (beanName) of all registered BeanDefinition;}} 3, BeanDefinitionParser
The responsibility of BeanDefinitionParser is to parse the XML element that defines schema in top-level. During parsing, we must access the XML element, so we can parse our custom XML content, such as the following example:
Package org.springframework.samples.xml; import org.springframework.beans.factory.support.BeanDefinitionBuilder;import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;import org.springframework.util.StringUtils;import org.w3c.dom.Element; import java.text.SimpleDateFormat; public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {1 protected Class getBeanClass (Element element) {return SimpleDateFormat.class 2} 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)) }}}
Note:
We use the AbstractSingleBeanDefinitionParser provided by Spring to handle some of the basic work of creating a BeanDefinition for a single.
We rewrote the doParse method of the AbstractSingleBeanDefinitionParser parent class to implement our own logic to create a BeanDefinition of type single.
4 、 Registering the handler and the schema
The coding work is finished. The following thing is that before the logic of Spring XML parsing can be woven into our defined element;, we need to register our custom namespaceHandler and custom XSD files in two specific target properties files. These properties files need to be placed under the 'META-INF' application' in your project.
1) META-INF/spring.handlers
This properties file is called 'spring.handlers', which contains the mapping of XML Schema URIS to namespace processing classes. In our example, we need something like the following:
Http\: / / www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler
The first part of the key-value pair (key) is the URI associated with your custom namespace extension and needs to match the 'targetNamespace' property' in your custom XSD Schema.
NamespaceHandlerResolver (DefaultNamespaceHandlerResolver) will find all the META-INF/spring.handlers files under the current classpath, load them, read the contents into namespaceURI to NamespaceHandler's map, and initialize all NamespaceHandler.
The resolve method of DefaultNamespaceHandlerResolver:
Public NamespaceHandler resolve (String namespaceUri) {Map handlerMappings = this.getHandlerMappings (); 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") } else {NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass (handlerClass); namespaceHandler.init (); handlerMappings.put (namespaceUri, namespaceHandler); return namespaceHandler } catch (ClassNotFoundException var7) {throw new FatalBeanException ("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", var7);} catch (LinkageError var8) {throw new FatalBeanException ("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", var8) } private Map getHandlerMappings () {if (this.handlerMappings = = null) {synchronized (this) {if (this.handlerMappings = = null) {try {Properties mappings = PropertiesLoaderUtils.loadAllProperties (this.handlerMappingsLocation, this.classLoader) If (this.logger.isDebugEnabled ()) {this.logger.debug ("Loaded NamespaceHandler mappings:" + mappings);} Map handlerMappings = new ConcurrentHashMap (mappings.size ()); CollectionUtils.mergePropertiesIntoMap (mappings, handlerMappings); this.handlerMappings = handlerMappings } catch (IOException var5) {throw new IllegalStateException ("Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", var5);} return this.handlerMappings;}
The loadAllProperties method in PropertiesLoaderUtils:
Public static Properties loadAllProperties (String resourceName, ClassLoader classLoader) throws IOException {Assert.notNull (resourceName, "Resource name must not be null"); ClassLoader classLoaderToUse = classLoader; if (classLoader = = null) {classLoaderToUse = ClassUtils.getDefaultClassLoader ();} Enumeration urls = classLoaderToUse! = null? ClassLoaderToUse.getResources (resourceName): ClassLoader.getSystemResources (resourceName); Properties props = new Properties (); while (urls.hasMoreElements ()) {URL url = (URL) urls.nextElement (); URLConnection con = url.openConnection (); ResourceUtils.useCachesIfNecessary (con); InputStream is = con.getInputStream () Try {if (resourceName.endsWith (".xml")) {props.loadFromXML (is);} else {props.load (is);}} finally {is.close ();}} return props;}
2) META-INF/spring.schemas
The properties file called 'spring.schemas', contains the location of the XML Schema in the classpath. This file is mainly to prevent spring from loading this schema file on the network. If you specify the properties file mapping, Spring will look for the schema in classpath (in this case, myns.xsd' under the org.springframework.samples.xml' package)
Http\: / / www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd
You'd better deploy your XSD files in the classpath of your NamespaceHandler and BeanDefinition classes.
5. Demonstration of the final effect
If you have completed the above steps, then you have successfully defined your own BeanDefinition in the Spring IOC container. We can get and use this Bean object by the Spring IOC container.
1) configuration Fil
Schema-beans.xml
2) Test class
SchemaBeanDefinitionTest.java
Package org.springframework.samples.xml; import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext; import java.text.SimpleDateFormat; public class SchemaBeanDefinitionTest {public static void main (String [] args) {ApplicationContext context = new ClassPathXmlApplicationContext ("schema-beans.xml"); SimpleDateFormat dateFormat = context.getBean ("dateFormat", SimpleDateFormat.class) System.out.println ("- gain object-"); System.out.println (dateFormat);}}
3) Project structure:
Note: static resources in idea must be placed under resources, otherwise an error will be reported.
4) running result:
The above is how to use Spring custom namespaces, and the editor believes that there are some knowledge points that we may see or use in our daily work. I hope you can learn more from this article. For more details, please follow the industry information channel.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.