Spring Bean definition Parsing source analysis

Source: Internet
Author: User
Tags addall aliases parse error throwable

In the introduction to the spring IOC container launch, a rough analysis of the launch process of the IOC container was made on the basis of Classpathxmlapplicationcontext, with no detailed description of some of the more complex steps, This article begins with some of the more complex steps that are analyzed. This article analyzes the parsing and loading process of the bean definition of the IOC container based on the Classpathxmlapplicationcontext. The simple sequence diagram of the bean definition parsing load is as follows:


The parsing of the bean definition is done by xmlbeandefinitionreader, and some preparations are made before parsing: 1. Set environment variables (environment) to match some placeholders that may appear in the bean configuration file; 2. Sets the XML definition file that the resource Locator uses to locate the bean, and 3 sets the parsing work for XML entity processor-assisted XML.

Xmlbeandefinitionreader Beandefinitionreader = new Xmlbeandefinitionreader (beanfactory);//Configure The Bean Definition reader with this context ' s//resource loading environment.beanDefinitionReader.setEnvironment ( This.getenvironment ()); Beandefinitionreader.setresourceloader (this); Beandefinitionreader.setentityresolver (new Resourceentityresolver (this));
Xmlbeandefinitionreader parses the bean's definition file into a DOM tree by Documentloader, and then parses each node in the DOM.

The Doloadbeandefinitions method of Xmlbeandefinitionreader

protected int doloadbeandefinitions (InputSource inputsource, Resource Resource) throws Beandefinitionstoreexception { try {int validationmode = Getvalidationmodeforresource (Resource);D ocument doc = this.documentLoader.loadDocument ( InputSource, Getentityresolver (), This.errorhandler, Validationmode, Isnamespaceaware ()); return Registerbeandefinitions (Doc, Resource);} catch (Beandefinitionstoreexception ex) {throw ex;} catch (Saxparseexception ex) {throw new Xmlbeandefinitionstoreexception (Resource.getdescription (), "line" + Ex.getlinenumber () + "in XML document from" + Resource + "is invalid", ex);} catch (Saxexception ex) {throw new Xmlbeandefinitionstoreexception (Resource.getdescription (), "XML document from" + Resource + "is invalid", ex);} catch (Parserconfigurationexception ex) {throw new Beandefinitionstoreexception (Resource.getdescription (), "Parser Configuration exception parsing XML from "+ Resource, ex);} catch (IOException ex) {throw new Beandefinitionstoreexception (resource.getdEscription (), "IOException parsing XML document from" + Resource, ex);} catch (Throwable ex) {throw new Beandefinitionstoreexception (Resource.getdescription (), "unexpected exception parsing XML document from "+ Resource, ex);}}
The next step is to parse the individual nodes in the DOM tree root node:

First, the parsing checks for the existence of the profile property, if the profile property is defined, the profile is determined to match the profile set to the bad state, and if the mismatch indicates that the bean defined by this subtree is not applicable to the current running bad, skipping the parsing of the current entire subtree, See Code snippets in the Doregisterbeandefinitions method of the Defaultbeandefinitiondocumentreader class

String Profilespec = Root.getattribute (Profile_attribute), if (Stringutils.hastext (Profilespec)) {Assert.state ( This.environment! = NULL, "Environment must is set for evaluating profiles"); string[] Specifiedprofiles = Stringutils.tokenizetostringarray (Profilespec, beandefinitionparserdelegate.multi_ Value_attribute_delimiters); if (!this.environment.acceptsprofiles (specifiedprofiles)) {return;}}
Loop through each subtree to check whether the node is the default namespace or an extended namespace, such as <bean>, <property> is the default namespace, and <aop:xxx>, <context:xxx> This label is an extension of the label required for additional label processors, the default label for the first level of labels <beans> First look at the default namespace.

The default namespace has several level two tags import, alias, Bean, beans.

Import tag, read the resource property, if not set parse error, determine whether the path resource corresponds to absolute or relative path, if the relative path needs to replace the path to the full path, and then recursive call Loadbeandefinitions

Beans tag, direct recursive call doregisterbeandefinitions

Alias tag, read the name and alias property, register the alias map in the container

The Processaliasregistration method of code in Defaultbeandefinitiondocumentreader

protected void Processaliasregistration (Element ele) {String name = Ele.getattribute (Name_attribute); String alias = Ele.getattribute (Alias_attribute); Boolean valid = True;if (! Stringutils.hastext (name)) {Getreadercontext (). Error ("Name must not is empty", ele); valid = false;} if (! Stringutils.hastext (alias)) {Getreadercontext (). Error ("Alias must Not is empty", ele); valid = false;} if (valid) {try {getreadercontext (). GetRegistry (). Registeralias (name, alias);} catch (Exception ex) {Getreadercontext (). Error ("Failed to register alias '" + Alias + "' for Bean with Name '" + name + "'" , Ele, ex);} Getreadercontext (). firealiasregistered (name, alias, Extractsource (Ele));}}
Bean label, parse the subtree under the bean tag is the most central action of the entire default namespace tag resolution, and the import and beans tags will eventually come to this step. The parsing logic for the Bean label is delegated to Beandefinitionparserdelegate:

First read the bean's ID attribute and the name attribute, respectively, the ID and alias of the code bean, name if separated by commas or semicolons, and split into an alias array. If the ID is not set, take the first alias as the ID and remove the alias from the alias Array.

public beandefinitionholder parsebeandefinitionelement (Element ele, beandefinition Containingbean) {String id = ele.getattribute (id_attribute); String nameattr = Ele.getattribute (Name_attribute); list<string> aliases = new arraylist<string> (); if (Stringutils.haslength (nameattr)) {string[] NameArr = Stringutils.tokenizetostringarray (nameattr, multi_value_attribute_delimiters); Aliases.addall (Arrays.asList ( Namearr));} String beanname = id;if (! Stringutils.hastext (beanname) &&!aliases.isempty ()) {beanname = Aliases.remove (0); if (logger.isdebugenabled ()) {Logger.debug ("No XML ' id ' specified-using '" + beanname + "' as Bean name and" + aliases + "as aliases");}.. .}
If the bean is not an internal bean, check if the ID and alias are already used by other beans, and if they are already occupied, parse the error. Otherwise, the IDs and aliases are placed in a Usednames collection attribute, declaring that the IDs and aliases are already occupied. The internal bean does not have to be checked because the ID of the internal bean is generated by the container, and the container guarantees that there will be no duplication. The Parsebeandefinitionelement method has a Containingbean parameter that distinguishes whether the currently parsed bean is an internal bean, and if the non-null description is an internal bean, Internal beans and non-internal bean parsing only do two different things: 1, the ID generation policy, 2, the internal bean needs to inherit the scope of its external bean.

protected void Checknameuniqueness (String beanname, list<string> aliases, Element beanelement) {string foundname = Null;if (Stringutils.hastext (beanname) && this.usedNames.contains (beanname)) {foundname = Beanname;} if (Foundname = = null) {Foundname = (String) collectionutils.findfirstmatch (this.usednames, aliases);} if (foundname! = null) {error ("Bean name '" + Foundname + "is already used in this <beans> element", beanelement); }this.usednames.add (Beanname); This.usedNames.addAll (aliases);}

If neither set ID nor alias is set, the container automatically generates an ID for the bean, and the following rule is generated:

1, whether the class is specified when the bean is defined, if the class name is specified, if unspecified, determines whether the parent is specified, if the parent name + $child is specified, if no factory bean is specified, if the specified fetch factory Bean name + $created, if none specified the description is configured with an error
2, if it is an internal bean, take step one generated name +#+ random number
3, if it is not an internal bean, if the name of step one generated does not exist in the container, take step one generated name, otherwise take step one generated name + #计数 if count has been used again count until count is not used

Public Beandefinitionholder parsebeandefinitionelement (Element ele, beandefinition containingbean) {... if ( Beandefinition! = null) {if (! Stringutils.hastext (Beanname)) {try {if (Containingbean! = null) {Beanname = Beandefinitionreaderutils.generatebeanname (Beandefinition, This.readerContext.getRegistry (), true);} else {beanname = This.readerContext.generateBeanName (beandefinition);//Register an alias for the plain Bean class name, I F still possible,//if the generator returned the class name plus a suffix.//this was expected for Spring 1.2/2.0 backward s compatibility. String beanclassname = Beandefinition.getbeanclassname (); if (beanclassname! = null &&beanname.startswith ( Beanclassname) && beanname.length () > Beanclassname.length () &&!this.readercontext.getregistry () . Isbeannameinuse (Beanclassname)) {Aliases.add (beanclassname);}} if (logger.isdebugenabled ()) {Logger.debug ("Neither XML ' id ' nor ' name ' specified-" + "using generated bean name [" + Bea Nname + "]");}}catch (Exception ex) {error (Ex.getmessage (), ele); return null;}} ...} return null;} public static String Generatebeanname (beandefinition definition, Beandefinitionregistry Registry, Boolean Isinnerbean) Throws Beandefinitionstoreexception {String generatedbeanname = Definition.getbeanclassname (); if (Generatedbeanname = = null) {if (definition.getparentname () = null) {Generatedbeanname = Definition.getparentname () + "$child";} else if (definition.getfactorybeanname () = null) {Generatedbeanname = Definition.getfactorybeanname () + "$created";}} if (! Stringutils.hastext (Generatedbeanname)) {throw new Beandefinitionstoreexception ("Unnamed bean definition Specifies Neither "+" ' class ' nor ' parent ' nor ' factory-bean '-can ' t generate bean name ');} String id = generatedbeanname;if (isinnerbean) {//Inner bean:generate identity Hashcode suffix.id = Generatedbeanname + Generated_bean_name_separator + objectutils.getidentityhexstring (definition);} else {//top-level Bean:use plain class name.//INCREase counter until the ID is unique.int counter = -1;while (counter = =-1 | | registry.containsbeandefinition (ID)) {counter ++;id = generatedbeanname + generated_bean_name_separator + counter;}} return ID;}

Then parse other properties and sub-tags such as class, parent, scope, lazy-init, Lookup-method, etc., to generate a Genericbeandefinition object, and encapsulating these attributes into the corresponding properties of the Genericbeandefinition object, the code is in the Parsebeandefinitionelement method of the Beandefinitionparserdelegate class:

Public abstractbeandefinition parsebeandefinitionelement (Element ele, String beanname, Beandefinition Containingbean) {This.parseState.push (new Beanentry (Beanname)); String className = null;if (Ele.hasattribute (Class_attribute)) {className = Ele.getattribute (Class_attribute). Trim (); try {String parent = null;if (Ele.hasattribute (Parent_attribute)) {parent = Ele.getattribute (Parent_attribute);} abstractbeandefinition bd = Createbeandefinition (className, parent);p arsebeandefinitionattributes (Ele, Beanname, Containingbean, BD); Bd.setdescription (Domutils.getchildelementvaluebytagname (Ele, description_element)); Parsemetaelements (Ele, BD);p arselookupoverridesubelements (Ele, Bd.getmethodoverrides ()); Parsereplacedmethodsubelements (Ele, Bd.getmethodoverrides ());p arseconstructorargelements (Ele, BD); Parsepropertyelements (Ele, BD);p arsequalifierelements (Ele, BD); Bd.setresource (This.readerContext.getResource ()); Bd.setsource (Extractsource (ele)); return BD;} catch (ClassNotFoundException ex) {error ("Bean class ["+ ClassName +"] not found ", Ele, ex);} catch (Noclassdeffounderror Err) {error ("class that Bean class [" + ClassName + "] depends on not found", ele, err);} catch (Throwable ex) {error ("Unexpected failure during bean definition parsing", ele, ex);} finally {This.parseState.pop ();} return null;}
Finally, if the decorator beandefinitiondecorator is defined and the bean definition is modified, the AOP tag defines the decorator, which is described in a later article, and then the parsed bean definitions and aliases are registered in the container.

protected void Processbeandefinition (Element ele, beandefinitionparserdelegate delegate) {Beandefinitionholder Bdholder = Delegate.parsebeandefinitionelement (ele); if (bdholder! = null) {Bdholder = Delegate.decoratebeandefinitionifrequired (Ele, bdholder); try {//Register the final decorated instance. Beandefinitionreaderutils.registerbeandefinition (Bdholder, Getreadercontext (). GetRegistry ());} catch (Beandefinitionstoreexception ex) {Getreadercontext (). Error ("Failed to register bean definition with name '" +bdhol Der.getbeanname () + "'", Ele, ex);} Send registration Event.getreadercontext (). firecomponentregistered (New Beancomponentdefinition (BdHolder));}} public static void Registerbeandefinition (Beandefinitionholder definitionholder, beandefinitionregistry Registry) Throws Beandefinitionstoreexception {//Register bean definition under primary name. String beanname = Definitionholder.getbeanname (); Registry.registerbeandefinition (Beanname, Definitionholder.getbeandefinition ());//Register ALiases for Beans name, if any. string[] aliases = definitionholder.getaliases (), if (aliases! = null) {for (String aliase:aliases) {Registry.registerali As (Beanname, aliase);}}}


The above is the default namespace of the label resolution, if the name Dom sub-tree root node namespace is extended, then enter the Beandefinitionparserdelegate parsecustomelement method, the approximate timing diagram is as follows:


First you need to find a namespace processor, and the mapping of namespaces and namespace processors is defined in the meta-inf/of the jar package The contents of the Spring.handlers file, such as Spring-context-xxxx.jar spring.handlers, are as follows:

Http\://www.springframework.org/schema/context=org.springframework.context.config.contextnamespacehandlerhttp\ ://www.springframework.org/schema/jee=org.springframework.ejb.config.jeenamespacehandlerhttp\:// www.springframework.org/schema/lang=org.springframework.scripting.config.langnamespacehandlerhttp\:// www.springframework.org/schema/task=org.springframework.scheduling.config.tasknamespacehandlerhttp\:// Www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler
We can see that the context namespace corresponds to the processing of the Contextnamespacehandler,jee namespace that the processor is jeenamespacehandler and so on. The parse method that calls the processor after the processor is found.

Public Beandefinition Parse (element element, ParserContext ParserContext) {return findparserforelement (element, ParserContext). Parse (element, parsercontext);} Private Beandefinitionparser Findparserforelement (element element, ParserContext parsercontext) {String LocalName = Parsercontext.getdelegate (). Getlocalname (Element); Beandefinitionparser parser = This.parsers.get (LocalName), if (parser = = null) {Parsercontext.getreadercontext (). Fatal ("Cannot locate beandefinitionparser for element [" + LocalName + "]", Element);} return parser;}
From the above code can be seen first to locate the tag corresponding to the parser, take Contextnamespacehandler as an example, from the Contextnamespacehandler code can be seen in the context namespace all the tags of the parser.

public class Contextnamespacehandler extends Namespacehandlersupport {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 ());}} 
Find the parser and then call the parser parse method to parse the specific label, such as the annotation-config tag parser is annotationconfigbeandefinitionparser, the code is as follows, Configurationclasspostprocessor, Autowiredannotationbeanpostprocessor, and other bpp are registered in the container when the annotation-config tag is encountered during parsing.
public class Annotationconfigbeandefinitionparser implements Beandefinitionparser {public Beandefinition parse ( Element element, ParserContext ParserContext) {Object Source = Parsercontext.extractsource (element);//Obtain Bean Definitions for all relevant beanpostprocessors.set<beandefinitionholder> processordefinitions = Annotationconfigutils.registerannotationconfigprocessors (Parsercontext.getregistry (), source);//Register Component for the surrounding <context:annotation-config> element. Compositecomponentdefinition compdefinition = new Compositecomponentdefinition (Element.gettagname (), source); Parsercontext.pushcontainingcomponent (compdefinition);//Nest the concrete beans in the surrounding component.for ( Beandefinitionholder processordefinition:processordefinitions) {parsercontext.registercomponent (new Beancomponentdefinition (Processordefinition));} Finally Register the composite component.parserContext.popAndRegisterContainingComponent (); return null;}}
The label on the extended namespace is not analyzed, and it is good to know how to find the relevant code.

Spring Bean definition Parsing source analysis

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.