Spring in-depth understanding 2 (about container source code)

Source: Internet
Author: User
Tags aliases xml dom document

The basic working principle of spring is as follows:


1. Find the bean configuration file

2. Load the bean configuration file and parse the generated intermediate representation BeanDefinition

3. Register beanDefinition

4. If it is a singleton or lazy-init = false, the bean is directly generated.



Spring will

1. Find the bean configuration file

2. Load the bean configuration file and parse the generated intermediate representation BeanDefinition
3. Register beanDefinition

These three sections provide more customization for users.

Spring abstracts file resources such as configuration files into a Resource and encapsulates methods such as getInputStream and isClose.

Resource has subclass ClasspathResouce and FileSystemResource

Resouce solves the problem of finding the bean configuration file in the first step.



Next we need to resolve the bean configuration file in step 2 and parse and generate the intermediate representation BeanDefinition.

Spring is implemented through BeanDefinitionReader (interface.

BeanDefinitionReader implements XmlBeanDefinitionReader.

XmlBeanDefinitionReader has a parameter that is used by the container BeanRegistry to store the parsed BeanDefinition.

That is, BeanDefinitionReader can complete the second step in three steps: 1. parse inputStream in Resource into xml Dom Form 2. Generate BeanDefinition according to Dom 3. register it in BeanRegistry

Here, parsing resource into Dom is done using another class defadocumentdocumentloader. Parse xml to generate BeanDefinition and register it, and hand it over to BeanDefinitionDocumentReader.


It can be seen that when spring designs the XmlBeanDefinitionReader class, it breaks down the responsibilities of this class and submits each part of the task to the corresponding class.



Example:

ClassPathResource res = new ClassPathResource ("containerTest. xml"); // locate the resource
DefaultListableBeanFactory factory = new DefaultListableBeanFactory (); // BeanRegistry container, which can be considered as providing a map for storing beanDefinition
XmlBeanDefinitionReader = new XmlBeanDefinitionReader (factory); // configure the read class

Int n = reader. loadBeanDefinitions (res); // triggered, XmlBeanDefinitionReader loads, parses the configuration file, and generates BeanDefinition actions

B B B = (B) factory. getBean ("B ");

The following is an analysis of the source code.

First, defalistlistablebeanfactory implements the BeanDefinitionRegistry interface, which represents a container that can store BeanDefinition.
XmlBeanDefinitionReader is a beanDefinition reader that completes configuration file locating, Loads Analysis, generates beanDefinition, and registers it to the container (these tasks are independently performed by different components ).
XmlBeanDefinitionReader already has a beanRegistry container element. Therefore, XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader (factory) only sets the container
To the registry attribute of XmlBeanDefinitionReader.
Next, call loadBeanDefinitions of XmlBeanDefinitionReader to start locating resources of ClassPathResource, loading analysis, and other subsequent operations.
The source code is as follows:
Public int loadBeanDefinitions (Resource resource) throws BeanDefinitionStoreException {
Return loadBeanDefinitions (new EncodedResource (resource ));
}

Here, we can see that the resource is encapsulated as EncodedResource, which encapsulates encoding for encoding when reading the stream.
Then, pass EncodedResource to the loadBeanDefinitions method.
Before reading the loadBeanDefinitions method, you may first ask yourself a question. What if containerTest. xml is imported in the current containerTest. xml file?
Theoretically, an error is reported. If so, how can this problem be solved?
The loadBeanDefinitions method is as follows:
Public int loadBeanDefinitions (EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert. notNull (encodedResource, "EncodedResource must not be null ");
If (logger. isInfoEnabled ()){
Logger.info ("Loading XML bean definitions from" + encodedResource. getResource ());
}

/**
The set here solves the problem of repeated resource file references.
Put EncodedResource into a set, and then put this set into
Among ThreadLocal: ThreadLocal > ResourcesCurrentlyBeingLoaded
Each time a new resource file is loaded, it is put into this set. If the new resource file already exists, an exception is thrown if the new resource file fails to be loaded.
**/
Set CurrentResources = this. resourcesCurrentlyBeingLoaded. get ();
If (currentResources = null ){
CurrentResources = new HashSet (4 );
This. resourcesCurrentlyBeingLoaded. set (currentResources );
}
If (! CurrentResources. add (encodedResource )){
Throw new BeanDefinitionStoreException (
"Detected cyclic loading of" + encodedResource + "-check your import definitions! ");
}

// If the current resource file does not exist, locate it, load the analysis, form a beanDefinition, and register it in the container.
Try {
InputStream inputStream = encodedResource. getResource (). getInputStream (); // get the resource file stream

Try {
InputSource inputSource = new InputSource (inputStream );
If (encodedResource. getEncoding ()! = Null ){
InputSource. setEncoding (encodedResource. getEncoding ());
}
Return doLoadBeanDefinitions (inputSource, encodedResource. getResource ());
}
Finally {
InputStream. close ();
}
}
Catch (IOException ex ){
Throw new BeanDefinitionStoreException (
"IOException parsing XML document from" + encodedResource. getResource (), ex );
}
Finally {
CurrentResources. remove (encodedResource );
If (currentResources. isEmpty ()){
This. resourcesCurrentlyBeingLoaded. remove ();
}
}
}

The code above indicates that in order to prevent repeated introduction of resource files, EncodedResource is put into a set, and then this set is put into
Among ThreadLocal: ThreadLocal > ResourcesCurrentlyBeingLoaded,
Each time a new resource file is loaded, it is put into this set. If the new resource file already exists, an exception is thrown if the new resource file fails to be loaded.

If not, perform subsequent operations. Subsequent operations are performed by doLoadBeanDefinitions.
The doLoadBeanDefinitions method is as follows:
Protected int doLoadBeanDefinitions (InputSource inputSource, Resource resource)
Throws BeanDefinitionStoreException {
Try {
Int validationMode = getValidationModeForResource (resource); // gets the xml Verification Mode, including xsd, dtd, and auto. Here is the xsd

// Load xml to form the xml Dom.
Document doc = this.doc umentLoader. loadDocument (
InputSource, getEntityResolver (), this. errorHandler, validationMode, isNamespaceAware ());

// Follow the xml Dom to parse the Dom, generate BeanDefinition, and register the Dom.
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 );
}
}

In this method, we can see the parsing file stream and generate the xml Dom document through the loadDocument of the DocumentLoader class. The responsibilities are more cohesive.
After the Dom is generated, call registerBeanDefinitions for subsequent operations.
The registerBeanDefinitions method is used to parse and generate a BeanDenition Based on the imported dom.
The registerBeanDefinitions method code is as follows:
Public int registerBeanDefinitions (Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader ();
DocumentReader. setEnvironment (this. getEnvironment ());
Int countBefore = getRegistry (). getBeanDefinitionCount ();
DocumentReader. registerBeanDefinitions (doc, createReaderContext (resource ));
Return getRegistry (). getBeanDefinitionCount ()-countBefore;
}
Method 1: BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader ();
A BeanDefinitionDocumentReader is created to parse the dom and register tasks. This also shows a style: feature cohesion.
XmlBeanDefinitionReader is currently in a coordinated combination, which allows a group of people to work together. Of course, XmlBeanDefinitionReader also performs some verification and initialization operations, as mentioned above, ensure that a resource file is loaded only once.
After documentReader is created, int countBefore = getRegistry (). getBeanDefinitionCount (); obtain the number of beanDefinition instances that have been registered in the current container.
Then, call registerBeanDefinitions of documentReader to perform subsequent operations. Return the number of beanDefinition (return getRegistry (). getBeanDefinitionCount ()-countBefore) for this registration ).
Next, let's continue to view the registerBeanDefinitions method. The Code is as follows.
Public void registerBeanDefinitions (Document doc, XmlReaderContext readerContext ){
This. readerContext = readerContext;

Logger. debug ("Loading bean definitions ");
Element root = doc. getDocumentElement ();

DoRegisterBeanDefinitions (root );
}
This method gets the root element in the doc file, which is [beans].
Call doRegisterBeanDefinitions for processing. The Code is as follows:
Protected void doRegisterBeanDefinitions (Element root ){
String profileSpec = root. getAttribute (PROFILE_ATTRIBUTE );
If (StringUtils. hasText (profileSpec )){
Assert. state (this. environment! = Null, "environment property must not be null ");
String [] specifiedProfiles = StringUtils. tokenizeToStringArray (profileSpec, describeandefinitionparserdelegate. MULTI_VALUE_ATTRIBUTE_DELIMITERS );
If (! This. environment. acceptsProfiles (specifiedProfiles )){
Return;
}
}

// Any nested Elements will cause recursion in this method. In
// Order to propagate and preserve Default-* attributes correctly,
// Keep track of the current (parent) delegate, which may be null. Create
// The new (child) delegate with a reference to the parent for fallback purposes,
// Then ultimately reset this. delegate back to its original (parent) reference.
// This behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this. delegate;
This. delegate = createHelper (readerContext, root, parent );

PreProcessXml (root );
ParseBeanDefinitions (root, this. delegate );
PostProcessXml (root );

This. delegate = parent; delimiter
}

As you can see, the code in the front is if (StringUtils. hasText (profileSpec), which is related to the profile function provided by spring, similar to the profile of maven.
We do not use profile here. We should jump
BeanDefinitionParserDelegate parent = this. delegate; and
This. delegate = createHelper (readerContext, root, parent );
A BeanDefinitionParserDelegate instance is created in these two statements to complete beanDenition generation and registration tasks.
PreProcessXml is used to process user-defined xml types. Because there is no preProcessXml, it is an empty method.
ParseBeanDefinitions (root, this. delegate); Complete beanDenition generation and registration. The Code is as follows:
Private void parseDefaultElement (Element ele, BeanDefinitionParserDelegate delegate ){
If (delegate. nodeNameEquals (ele, IMPORT_ELEMENT )){
// If it is an import resource file, perform the import resource file Logic
ImportBeanDefinitionResource (ele );
}
Else if (delegate. nodeNameEquals (ele, ALIAS_ELEMENT )){
// If the element is alias
ProcessAliasRegistration (ele );
}
Else if (delegate. nodeNameEquals (ele, BEAN_ELEMENT )){
// If the element is bean, beanDefinition and registration are generated.
ProcessBeanDefinition (ele, delegate );
}
Else if (delegate. nodeNameEquals (ele, NESTED_BEANS_ELEMENT )){
// Recurse
DoRegisterBeanDefinitions (ele );
}
}

This method focuses on bean element processing. It is in the else if (delegate. nodeNameEquals (ele, BEAN_ELEMENT) {branch.
ProcessBeanDefinition is called for processing. The Code is as follows:
/**
* Process the given bean element, parsing the bean definition
* And registering it with the registry.
*/
Protected void processBeanDefinition (Element ele, BeanDefinitionParserDelegate delegate ){
// Delegate parses xml elements, generates beanDenition, and puts it into BeanDefinitionHolder.
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'" +
BdHolder. getBeanName () + "'", ele, ex );
}
// Send registration event.
GetReaderContext (). fireComponentRegistered (new BeanComponentDefinition (bdHolder ));
}
}

We can see that this method uses delegate. parseBeanDefinitionElement (ele) to generate BeanDefinition (resolution Such a label element puts its value in the generated BeanDefinition instance, and puts it in BeanDenitionHolder.
Next, let's focus on
// Register the final decorated instance.
BeanDefinitionReaderUtils. registerBeanDefinition (bdHolder, getReaderContext (). getRegistry ());
This is to register beanDenition to the container. BeanDefinitionReaderUtils. registerBeanDefinition code is as follows:
Public static void registerBeanDefinition (
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry)
Throws BeanDefinitionStoreException {

// Register bean definition under primary name.
String beanName = definitionHolder. getBeanName ();
Registry. registerBeanDefinition (beanName, definitionHolder. getBeanDefinition ());

// Register aliases for bean name, if any.
String [] aliases = definitionHolder. getAliases ();
If (aliases! = Null ){
For (String aliase: aliases ){
Registry. registerAlias (beanName, aliase );
}
}
}

From this, we can see that the registry. registerBeanDefinition (beanName, definitionHolder. getBeanDefinition () method is called for registration ());
In registry. registerBeanDefinition, logic such as beanDenition and whether to allow overwriting will exist. In the end, this method will synchronize the map stored in beanDenition
BeanDenition's name is key, VALUE is beanDenition, and put it into this map.
The Code is as follows:

Public void registerBeanDefinition (String beanName, BeanDefinition beanDefinition)
Throws BeanDefinitionStoreException {

Assert. hasText (beanName, "Bean name must not be empty ");
Assert. notNull (beanDefinition, "BeanDefinition must not be null ");

If (beanDefinition instanceof AbstractBeanDefinition ){
Try {
(AbstractBeanDefinition) beanDefinition). validate ();
}
Catch (BeanDefinitionValidationException ex ){
Throw new BeanDefinitionStoreException (beanDefinition. getResourceDescription (), beanName,
"Validation of bean definition failed", ex );
}
}

Synchronized (this. beanDefinitionMap ){
// If the old beanDenition with the same name already exists, check whether overwriting is allowed. An error cannot be thrown directly.
Object oldBeanDefinition = this. beanDefinitionMap. get (beanName );
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 (this. logger. isInfoEnabled ()){
This.logger.info ("Overriding bean definition for bean '" + beanName +
"': Replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
Else {
This. beanDefinitionNames. add (beanName );
This. frozenBeanDefinitionNames = null;
}
// Put beanDenition into map. The key is the bean name and the value is beanDenition.
This. beanDefinitionMap. put (beanName, beanDefinition );
}

ResetBeanDefinition (beanName );
}

Related Article

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.