In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/02 Report--
How to load and parse the configuration file, in view of this problem, this article introduces the corresponding analysis and solution in detail, hoping to help more partners who want to solve this problem to find a more simple and easy way.
Loading of bean-parsing of configuration file
Spring is a framework with many modules. There are four core parts, bean,core,context and Expresion Language.
Core and bean are the basic modules of spring. Provides Ioc (inversion of Control) and dependency injection features, the basic concept of which is BeanFactory. The bean factory, a factory that can create a variety of bean. When we use spring to work on a project, the bean,Bean module contains all the classes related to accessing profiles, creating and managing bean, and performing Ioc and DI operations.
Since bean is so important, it makes sense to analyze the loading process of bean. Only analyze and compare the core, can think of some of the details according to these methods, so some places will not be too in-depth, suitable for readers who have a certain understanding of spring, in-depth study will be left to their own research.
As mentioned above, the definition of bean is so simple, but what is the loading process? For simplicity, the obsolete subclass XmlBeanFactory of DefaultListableBeanFactory will be used for analysis, and the source code is spring 5.1.5.RELEASE. This is Learning Engineering.
Core class DefaultListableBeanFacory
First give a class diagram, understand, have an impression, later will be gradually familiar with.
AliasRegistry: define operations such as annual increment and deletion of alias; SimpleAliasRegistry: mainly use map as the cache of alias and implement the interface AliasRegistry; SingletonBeanRegistry: define the registration and acquisition of singletons; BeanFactory: define the various properties of bean and bean; DefaultSingletonBeanRegistry: implement the functions of interface SingletonBeanRegistry; hierarchicalBeanFactory: inherit BeanFactory, that is, add support for ParrentFactory based on the functions defined by beanFactory BeanDefintionRegistry: define various additions, deletions and modifications to BeanDefinition; FactoryBeanRegistrySupport: add special processing features to FactoryBean based on DefaultSingletonBeanRegistry; ConfigurableBeanFactory: provide various methods for configuring Factory; ListableBeanFactory: obtain the configuration list for filing according to various conditions; AbstractBeanFactory: integrate the functions of FactoryBeanRegistrySupport and ConfigurableBeanFactory. AutowireCapableBeanFacotry: provides post-processor for creating bean, automatically injecting, initializing and applying bean; AbstractAutowireCapableBeanFactory: synthesizes AbstractBeanFactory and implements interface AutowireCapableBeanFactory; ConfigurableListableBeanFactory: BeanFactory configuration list, specifies Hu lie type and interface, etc.; DefaultListableBeanFactory: integrates all the functions above, mainly processing after Bean registration
XmlBeanFactory is a subclass of DefaultListableBeanFactory and is marked as obsolete with the addition of the reader attribute of type XmlBeanDefinitionReader. Although it has been marked as obsolete, even DefaultListableBeanFactory is eventually read by XmlBeanDefinitionReader during the initialization of spring. Therefore, it does not affect our analysis of the code.
XmlBeanDefinitionReader
The configuration file for spring is the most common way to implement the bean configuration (springboot is not discussed here), and it is XmlBeanDefinitionReader that reads the xml configuration file. Let's first take a look at some of the class functions that will be used.
ResourceLoader: defines a resource loader, which is mainly used to return your Resource;BeanDefinitionReader according to a given resource file address: mainly defines the functions of reading resource files and converting them to BeanDefinition; EnvironmentCapable: defines the functions of getting Environment; DocumentLoader: defines the function of loading from resource files to conversion to Document; AbstractBeanDefinitionReader: implements the functions defined by the EnvironmentCapable and BeanDefinitionReader classes; BeanDefinitionDocumentReader: defines the read Document and registers the BeanDefinition function BeanDefinitionParserDelegate: define various methods for parsing Element
XmlBeanDefinitionReader uses ResourseLoader to convert the path of resource files into corresponding Resource files by inheriting the methods in AbstractBeanDefinitionReader; converts Resource files into Document files by DocumentLoader; parses Document by implementing the DefaultBeanDefinitionDocumentReader class of interface BeanDefinitionDocumentReader, and parses Element with BeanDefinitionParserDelegate.
Xml file reads public XmlBeanFactory (Resource resource, BeanFactory parentBeanFactory) throws BeansException {super (parentBeanFactory); / / starts loading and reading xml file this.reader.loadBeanDefinitions (resource);}
In the constructor, super calls the parent class, and you will find the following code in AbstractAutowireCapableBeanFactory:
Public AbstractAutowireCapableBeanFactory () {super (); / / ignores the automatic assembly function of the specified interface ignoreDependencyInterface (BeanNameAware.class); ignoreDependencyInterface (BeanFactoryAware.class); ignoreDependencyInterface (BeanClassLoaderAware.class);}
Go back to the this.reader.loadBeanDefinitions (resource) of the constructor
/ / XmlBeanDefinitionReaderpublic int loadBeanDefinitions (Resource resource) throws BeanDefinitionStoreException {return loadBeanDefinitions (new EncodedResource (resource));} public int loadBeanDefinitions (EncodedResource encodedResource) throws BeanDefinitionStoreException {/ check the current thread is loading resource Set currentResources = this.resourcesCurrentlyBeingLoaded.get (); if (currentResources = = null) {currentResources = new HashSet (4); this.resourcesCurrentlyBeingLoaded.set (currentResources) } / / if the encodeResource currently being parsed is loading, throw an exception if (! currentResources.add (encodedResource)) {throw new BeanDefinitionStoreException ("Detected cyclic loading of" + encodedResource + "- check your import definitions!") } try {/ / get the inputStream InputStream inputStream of resource = encodedResource.getResource () .getInputStream (); try {/ / is packaged as inputSource InputSource inputSource = new InputSource (inputStream) If (encodedResource.getEncoding ()! = null) {inputSource.setEncoding (encodedResource.getEncoding ());} / / start loading return doLoadBeanDefinitions (inputSource, encodedResource.getResource ()) } finally {inputStream.close () }} / /.} protected int doLoadBeanDefinitions (InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {try {/ / convert resource to document. Here the header will verify the xml file, DTD or XSD. Different types will use different parsing methods. Go and see Document doc = doLoadDocument (inputSource, resource) if you like. / / parse document and register bean int count = registerBeanDefinitions (doc, resource); / / return the number of bean loaded return count;}} public int registerBeanDefinitions (Document doc, Resource resource) throws BeanDefinitionStoreException {/ / create document parser BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader (); int countBefore = getRegistry (). GetBeanDefinitionCount () / / use document parser to parse document / / here createReaderContext () creates XmlReaderContext documentReader.registerBeanDefinitions (doc, createReaderContext (resource)); / / * ① return getRegistry (). GetBeanDefinitionCount ()-countBefore;} public XmlReaderContext createReaderContext (Resource resource) {return new XmlReaderContext (resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, getNamespaceHandlerResolver ()) } public NamespaceHandlerResolver getNamespaceHandlerResolver () {if (this.namespaceHandlerResolver = = null) {this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver ();} return this.namespaceHandlerResolver;} protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver () {ClassLoader cl = (getResourceLoader ()! = null? GetResourceLoader () .getClassLoader (): getBeanClassLoader (); / / returns the default namespace parser, which is used to get the NamespaceHandler return new DefaultNamespaceHandlerResolver (cl) of the parsing tag;}
See the method * ①:
/ / DefaultBeanDefinitionDocumentReader.javapublic void registerBeanDefinitions (Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext; / / parse document doRegisterBeanDefinitions (doc.getDocumentElement ());} protected void doRegisterBeanDefinitions (Element root) {BeanDefinitionParserDelegate parent = this.delegate; / / create a default BeanDefinitionParserDelegate to parse the custom tag this.delegate = createDelegate (getReaderContext (), root, parent) / /. Profile processing, if profile is defined in web.xml, only the corresponding file if (this.delegate.isDefaultNamespace (root)) {String profileSpec = root.getAttribute (PROFILE_ATTRIBUTE) is parsed. If (StringUtils.hasText (profileSpec)) {String [] specifiedProfiles = StringUtils.tokenizeToStringArray (profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); / / We cannot use Profiles.of (...) Since profile expressions are not supported / / in XML config. See SPR-12458 for details. If (! getReaderContext (). GetEnvironment (). AcceptsProfiles (specifiedProfiles)) {if (logger.isDebugEnabled ()) {logger.debug ("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching:" + getReaderContext () .getResource () } return;} / / leave subclass extension preProcessXml (root); / / really start parsing parseBeanDefinitions (root, this.delegate); / / leave subclass extension postProcessXml (root) } 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)) { //解析默认标签 parseDefaultElement(ele, delegate); } else { //解析自定义标签 delegate.parseCustomElement(ele); } } } } else { //解析自定义标签 delegate.parseCustomElement(root); }}解析默认标签private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { //解析import标签,如果有import标签,其实就会递归解析了 importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { //解析别名标签 processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { //解析bean标签 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // 解析beans标签,其实就是把当前节点继续丢去解析而已,产生了递归 doRegisterBeanDefinitions(ele); }} 对默认标签的解析就不多写了,无非不过就是怎么去读取文件,怎么解决文件中的各种数据,喜欢的就自己深究下去吧,再细下去就太多了,为了给自己一个印象,我这里不会深究下去。 解析自定义标签 先看个demo吧。 指定命名空间myname="http://www.wt.com.schema/user",看下面META-INF/Spring.handlers配置定义了当前命名空间所使用的解析器;schemaLocation中的http://www.wt.com/schema/user.xsd指定文件所在目录,看下面META-INF/Spring.schemas的配置,指定了xsd的文件所在目录。 # META-INF/Spring.handlers# 定义指定namespace解析器,格式namespace = NamespaceHandlerhttp\://www.wt.com/schema/user=com.wt.test.customtag.MyNamespaceHandler# META-INF/Spring.schemas# 定义xml约束文档xsd所在目录http\://www.wt.com/schema/user.xsd=META-INF/user.xsd //注册自定义的命名空间解析器public class MyNamespaceHandler extends NamespaceHandlerSupport { public void init() { //注册解析器 registerBeanDefinitionParser("user", new UserBeanDefinitionParser()); }}//该方法在父类NamespaceHandlerSupport中,父类实现了NamespaceHandlerpublic BeanDefinition parse(Element element, ParserContext parserContext) { BeanDefinitionParser parser = findParserForElement(element, parserContext); return (parser != null ? parser.parse(element, parserContext) : null);}//标签解析器public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class getBeanClass(Element element) { return User.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder bean) { String name = element.getAttribute("name"); String age = element.getAttribute("age"); String email = element.getAttribute("email"); if (StringUtils.hasText(name)) { bean.addPropertyValue("name", name); } if (StringUtils.hasText(age)) { bean.addPropertyValue("age", age); } if (StringUtils.hasText(email)) { bean.addPropertyValue("email", email); } }} 以上就是所有的自定义标签的配置。实现NamespaceHandler,在init方法中注册我们自定义的解析器。其实逻辑只是我们调用namespaceHandler的parse方法来解析自定义标签。但真正的解析肯定就是在parse方法中调用我们自己的解析器来解析而已。 现在开始分析,自定义标签是通过delegate.parseCustomElement(ele)来解析的。dalegate就是注释中提到的创建的默认的BeanDefinitionParserDelegate。 //BeanDefinitionParserDelegate.javapublic BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null);}public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) { //获取namespaceURI,也就是我们定义的命名空间http://www.wt.com/schema/user String namespaceUri = getNamespaceURI(ele); if (namespaceUri == null) { return null; } //使用nameSpaceHandlerResolver,根据namespaceUri获取自定义的标签解析器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 handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));} 我们自定义的MyNamespaceHandler是就是通过NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);这一行来获取的。直接跟进去,进入resolve方法。至于getNamespaceHandlerResolver()在哪里创建的之前提到过,这里就不继续说了,是默认的。 //DefaultNamespaceHandlerResolver.javapublic NamespaceHandler resolve(String namespaceUri) { //获取所有的命名空间->The mapping of the namespace parser is the mapping in Spring.handlers Map handlerMappings = getHandlerMappings (); Object handlerOrClassName = handlerMappings.get (namespaceUri); if (handlerOrClassName = = null) {return null } / / it is String- > String when it is loaded for the first time, but if it has been obtained, it will be instantiated to become String- > NamespaceHandler and add the cache. / / so let's first determine whether it has been initialized. If so, there is no need to initialize else if (handlerOrClassName instanceof NamespaceHandler) {return (NamespaceHandler) handlerOrClassName. } else {/ / load for the first time, initialize String className = (String) handlerOrClassName; try {Class handlerClass = ClassUtils.forName (className, this.classLoader) based on the fully qualified class name; / /. NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass (handlerClass); / / execute the init method, in which our custom namespaceHandler registers our own parser namespaceHandler.init (); / / adds the current instance to avoid instantiating handlerMappings.put (namespaceUri, namespaceHandler) again next time Return namespaceHandler;} / /...} private Map getHandlerMappings () {Map handlerMappings = this.handlerMappings; if (handlerMappings = = null) {synchronized (this) {handlerMappings = this.handlerMappings If (handlerMappings = = null) {/ /... Try {/ / get the mapping relationship Properties mappings = PropertiesLoaderUtils.loadAllProperties (this.handlerMappingsLocation, this.classLoader) from the file; / /. HandlerMappings = new ConcurrentHashMap (mappings.size ()); CollectionUtils.mergePropertiesIntoMap (mappings, handlerMappings); this.handlerMappings = handlerMappings } / /...} return handlerMappings;} public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers"; public DefaultNamespaceHandlerResolver () {this (null, DEFAULT_HANDLER_MAPPINGS_LOCATION);}
In the getHandlerMappings () method, we notice this line of code Properties mappings = PropertiesLoaderUtils.loadAllProperties (this.handlerMappingsLocation, this.classLoader); so the code gets all the mappings from the Spring.handlers file. At the end of the above code, the default address, META-INF/Spring.handlers, is affixed.
So we get our custom NamespaceHandler, and then we call our MyNameSpaceHandler's parse method to parse (in fact, here the head will call our own parser UserBeanDefinitionParser. Oh, it's easy to see for yourself).
The spring-aop we are familiar with is achieved through custom tags, using AopNamespaceHandler. And springmvc, too, using MvcNamespaceHandler.
The parsing content of xml is relatively small, but it seems difficult if you are not familiar with spring. The focus here is to give yourself a rough outline, in which you can recall the details.
This is the answer to the question about how to load and parse the configuration file. I hope the above content can be of some help to you. If you still have a lot of doubts to be solved, you can follow the industry information channel for more related knowledge.
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.