Spring source code reading notes (3)

Before the report navigation

Spring Source Code reading notes (1) Spring source code reading notes (2)

Prospects for review

In the previous chapter, we walked into the Refresh method and read through the prepareRefresh method, where we did a couple of things

  1. Some status bits are set
  2. There is an extension method to add parameters to the propertySources
  3. Verify the required RequireProperty
  4. Set some observers, do not know what is used

Then I started reading obtainFreshBeanFactory methods and summarized them briefly

  1. Create a DefaultListableBeanFactory
  2. Set some dependency interfaces for BeanFactory to be ignored
  3. The policy for instantiating beans is set, or Cglib if not
  4. Set two values for the factory, the most important one to note later
    1. AllowBeanDefinitionOverriding allows Bean definitions to rewrite
    2. AllowCircularReferences allows circular references

So far, let’s take a look at some method call flowcharts in obtainFreshBeanFactory.The loadBeanDefinitions method was introduced at the end of an article, and then we started.

loadBeanDefinitions

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   Create a new XmlBeanDefinitionReader for the given BeanFactory.
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // Configure the bean definition reader with this context's
   // resource loading environment.
   // Configure the bean to define the reader using the resource loading environment of this context.
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // Allow a subclass to provide custom initialization of the reader,
   // then proceed with actually loading the bean definitions.
   // Allow subclasses to provide a custom initialization of the reader and then proceed with the actual loading of the bean definition.
   initBeanDefinitionReader(beanDefinitionReader);
   loadBeanDefinitions(beanDefinitionReader);
}
Copy the code
  1. An XmlBeanDefinitionReader is created
  2. I set some properties for this Reader
  3. Read Bean definition information.

Let’s take a look at the XmlBeanDefinitionReader constructor

First code


XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
   super(registry);
}


protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
   this.registry = registry;

   // Determine ResourceLoader to use.
   // Identify the ResourceLoader to use.
   if (this.registry instanceof ResourceLoader) {
      this.resourceLoader = (ResourceLoader) this.registry;
   }
   else {
       / / our registry is DefaultListableBeanFactory here, he didn't inherit from ResourceLoader
       // So the else operation will be entered
      this.resourceLoader = new PathMatchingResourcePatternResolver();
   }

   // Inherit Environment if possible
   // Inherit the environment as much as possible
   if (this.registry instanceof EnvironmentCapable) {
      this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
   }
   else {
      this.environment = newStandardEnvironment(); }}Copy the code

It is not hard to see from the above code, a member variable of AbstractBeanDefinitionReader, resourceLoader finally assigned to PathMatchingResourcePatternReslover, literal meaning to understand the path matching resource parser, As for what to do, later encountered, in the first to see the record.

The comment at the top of the code reads, “Inherit the environment as much as possible. Then, it is necessary to determine whether the current Registry has the ability of the environment. If not, there is no doubt. DefaultListableBeanFactory does not implement this interface, so it will enter the else.

One thing to note here is that the environment and applicationContext are not the same as they seem to be. As for whether it will merge or not, we will see later.

Second code

// Configure the bean definition reader with this context's
// resource loading environment.

// Configure the bean to define the reader using the ResourcesLoadd and Environment of the current Context.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
Copy the code

See the code, just also distinguish the Environment here is merged, because AbstractXmlApplicatonContext inherited from ResourceLoader interface, so the equivalent to the current class also have the ability to ResourceLoader.

New ResourceEntityResolver (ResourceEntityResolver, ResourceEntityResolver, ResourceEntityResolver, ResourceEntityResolver);

public ResourceEntityResolver(ResourceLoader resourceLoader) {
   super(resourceLoader.getClassLoader());
   this.resourceLoader = resourceLoader;
}

public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
   this.dtdResolver = new BeansDtdResolver();
   this.schemaResolver = new PluggableSchemaResolver(classLoader);
}
Copy the code

There are two parsers set up here, which seem to be used to parse Xml DTDS and so on.

The third code

initBeanDefinitionReader(beanDefinitionReader);

protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
   reader.setValidating(this.validating);
}
Copy the code

Note that a Validating value is set here.

The fourth code

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();
   if(configResources ! =null) {
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations();
   if(configLocations ! =null) { reader.loadBeanDefinitions(configLocations); }}Copy the code

GetConfigResources here is a template method, rewrite the, in the ClassPathXmlApplicationContext getConfigResources, because ConfigResource so far not been set, so empty.

GetConfigLocations does not get empty, because it was resolved in section 1, and setConfigLocations is called before refresh, where If is True.

@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
   Assert.notNull(locations, "Location array must not be null");
   int count = 0;
   for (String location : locations) {
      count += loadBeanDefinitions(location);
   }
   return count;
}

@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
   return loadBeanDefinitions(location, null);
}

Copy the code

As you can see from the above, the key is loadBeanDefinitions. The location we get here is the application.xml that we used to launch the class Settings.

So let’s go back to the code

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
   ResourceLoader resourceLoader = getResourceLoader();
   if (resourceLoader == null) {
      throw new BeanDefinitionStoreException(
            "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
   }
    // 
   if (resourceLoader instanceof ResourcePatternResolver) {
      // Resource pattern matching available.
      // Resource pattern matching is available.
      try {
         Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
         int count = loadBeanDefinitions(resources);
         if(actualResources ! =null) {
            Collections.addAll(actualResources, resources);
         }
         if (logger.isTraceEnabled()) {
            logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
         }
         return count;
      }
      catch (IOException ex) {
         throw new BeanDefinitionStoreException(
               "Could not resolve bean definition resource pattern [" + location + "]", ex); }}else {
      // Can only load single resources by absolute URL.
      // Only a single resource can be loaded via an absolute URL.
      Resource resource = resourceLoader.getResource(location);
      int count = loadBeanDefinitions(resource);
      if(actualResources ! =null) {
         actualResources.add(resource);
      }
      if (logger.isTraceEnabled()) {
         logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
      }
      returncount; }}Copy the code

The getResourceLoader method is used to obtain a ResourceLoader, which is the ApplicationContext. The class diagram inherits from the ResourcePatternResolver interface.

This code is a little long, so let’s break it down.

LoadBeanDefinitions code Unpacking (1)

Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
Copy the code

The getResource logic is a bit too long to follow, and the code is too bloated to follow, so I’m just going to take a quick look at it and summarize it, and it’s going to return a ClassPathContextResource that inherits from the ClassPathResource, which has two member variables, A call this, one is called path, by experienced can see out here, if you want to get this file, you can through this. GetResourceAsStream () to obtain.

LoadBeanDefinitions code Unpacking (2)

int count = loadBeanDefinitions(resources);

@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
   Assert.notNull(resources, "Resource array must not be null");
   int count = 0;
   for (Resource resource : resources) {
      count += loadBeanDefinitions(resource);
   }
   return count;
}

int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
Copy the code

Since location was converted to the exact Resource in the last debunking. LoadBeanDefinitions should be used again according to Resources. So the code goes down, and then it ends up in the BeanDefinitionReader interface, so look at its implementation class.

At this point, we found that there were three implementation classes whose names looked familiar, corresponding to each of our configuration file types.

Remember that we created an XmlBeanDefinitionReader when we first entered the loadBeanDefinitions method. Go to the class and see the implementation.

@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   return loadBeanDefinitions(new EncodedResource(resource));
}

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   Assert.notNull(encodedResource, "EncodedResource must not be null");
   if (logger.isTraceEnabled()) {
      logger.trace("Loading XML bean definitions from " + encodedResource);
   }

   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

   if(! currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }

   try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
      InputSource inputSource = new InputSource(inputStream);
      if(encodedResource.getEncoding() ! =null) {
         inputSource.setEncoding(encodedResource.getEncoding());
      }
      return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
   }
   finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
         this.resourcesCurrentlyBeingLoaded.remove(); }}}Copy the code

This method is relatively simple. It shows that we took a HashSet from a Thread, put a Resource into the Set, and then got the InputStream of the current Resource. InputSource is a temporary storage class. DoLoadBeanDefinitions is subsequently called.

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {

   try {
      Document doc = doLoadDocument(inputSource, resource);
      int count = registerBeanDefinitions(doc, resource);
      if (logger.isDebugEnabled()) {
         logger.debug("Loaded " + count + " bean definitions from " + resource);
      }
      return count;
   } catch(...) {// Too much Catch. }}Copy the code

The most important part of this method is not parsing Xml, but registering beans. The core method is registerBeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   int countBefore = getRegistry().getBeanDefinitionCount();
   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());
}

Copy the code

Methods at first created a BeanDefinitionDocumentReader, specially used to read Xml class. DefaultListableBeanFactory getRegistry finally got here, this time is empty in the factory. Before entering the registration method, a context is created with an incoming event listener and itself (ApplicationContext). Next comes the key method

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
   this.readerContext = readerContext;
   doRegisterBeanDefinitions(doc.getDocumentElement());
}

protected void doRegisterBeanDefinitions(Element root) {
  
   / /... What's a bunch of judgments going to do

   preProcessXml(root);
   parseBeanDefinitions(root, this.delegate); // Key code
   postProcessXml(root);

   this.delegate = parent;
}
Copy the code

The key piece of code in this section is parseBeanDefinitions, and look at the method name transformation Bean definition information. Take a closer look here.

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 ele) {
            if (delegate.isDefaultNamespace(ele)) {
               parseDefaultElement(ele, delegate);
            }
            else{ delegate.parseCustomElement(ele); }}}}else{ delegate.parseCustomElement(root); }}Copy the code

As you can see from this method, this is a standard judgment for parsing XML nodes. One of the methods in this method is parseDefaultElement, which literally translates the default node,

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate); // Bean label policy
   }
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recursedoRegisterBeanDefinitions(ele); }}Copy the code

As you can see here, a typical policy pattern. Now go to the Bean label’s policy and look at the code.

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.
         // Register the final decorator 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.
      // Send the registration event.
      getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder)); }}Copy the code

Two official notes stand out

  1. Register the final decorator instance.
  2. Send the registration time.

Look at the code for each of these two pieces

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

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

   // Register aliases for bean name, if any.
   // Register an alias for the bean name (if any).
   String[] aliases = definitionHolder.getAliases();
   if(aliases ! =null) {
      for(String alias : aliases) { registry.registerAlias(beanName, alias); }}}Copy the code

The code above tells us that we call the registerBeanDefinition method in Registry to register a BeanDefinition in the factory, and I’m going to go through the method in a separate article because it’s a bit complicated.

Come to an end

At this point, the obtainFreshBeanFactory method is over for now. In the next article, I’ll take a closer look at the registerBeanDefinition method in Factory. The class diagram covered so far is posted below.

conclusion

The purpose of writing an article is to help you consolidate your knowledge. You can point out your bad or wrong writing in the comments section. If you read the article and think it is helpful to you, you can like it. If you find some questions and have doubts, or do not understand, you can comment. Of course, I also hope to make friends with you and learn from each other.