Spring IOC Views Source Code from Example

Posted by alanho on Fri, 17 May 2019 14:21:55 +0200

Spring IOC Views Source Code from Example

A more complete portal for notes Github

Example

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.login();
    }
}

public interface UserService {
    void login();
}

@Service("userService")
public class UserServiceImpl implements UserService{
    @Override
    public void login() {
        System.out.println("login...");
    }
}

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        ">

    <context:component-scan
            base-package=
                    "cn.sinjinsong.ioc"/>
</beans>

Appointment

Method invocation sequence Convention

Here 1.1 and 1.2 are used to represent the more important steps 1 and 2 in the 1) method.
In addition, it is not from the beginning to number, but from the important method to start numbering.

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.login();
    }
}

For example, if the main method in the Main class is marked as 1, you can mark the new ClassPathXml Application Context as 1.1 and the getBean as 1.2.

Concern constraints

We only focus on the core business logic in the source code, and because Spring is too complex, it's not easy to master even its core logic. In addition, the article looks at the source code from the example, so there may be multiple execution paths in the source code, but we only focus on the logic in the instance, other execution paths may be ignored.

IOC overview

IOC is essentially Spring manages a container, which is the implementation of BeanFactory, which manages all user-specified bean s (xml or annotations), and at the bottom is a Map.

Registration of bean s

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");

Called (called)

public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }

called

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
        throws BeansException {

    super(parent);
    setConfigLocations(configLocations);
    if (refresh) {
        refresh();
    }
}

From here on, one of the most important ways to register bean s is refresh.
This method will be called not only in normal Java projects, but also in Web Application Context used in Java Web projects. We must pay attention to this method.
The refresh method completes the whole loading process of the bean Factory.

public void refresh() throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Preparing a refreshed context
      prepareRefresh();

      // Initialize BeanFactory and read the XML file
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Fill in BeanFactory with various functions
      prepareBeanFactory(beanFactory);

      try {
         // Subclass Overlay Method for Additional Processing
         postProcessBeanFactory(beanFactory);

         // Activate various BeanFactory processors
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register to intercept the Bean processor created by the Bean. This is just registration. The real call is at getBean time.
         registerBeanPostProcessors(beanFactory);

         // Initialization of Message Sources for Context, i.e. Message Body in Different Languages, Internationalization Processing
         initMessageSource();

         // Initialize the application message broadcaster and place it in the application Event Multicaster bean
         initApplicationEventMulticaster();

         // Leave subclasses to initialize other bean s
         onRefresh();

         // Find Listener bean s in all registered beans and register them in message broadcasters
         registerListeners();

         // Initialize the remaining singleton instances (except lazy-init)
         finishBeanFactoryInitialization(beanFactory);

         // Complete the refresh process, notify the lifecycle processor of the refresh process, and issue ContextRefreshEvent to notify others
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

Two important methods are obtainFreshBeanFactory, which reads all bean Definitions (such as beans defined in xml configuration files, or beans marked with @Component,@Service, etc.) and saves them to bean Factroy; and FinishBeanFactory Initialization, which loads all non-delayed, singleton beans.
Number the method from here.

1) obtainFreshBeanFactory (load bean Definition)

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   refreshBeanFactory();
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
   }
   return beanFactory;
}
//The main logic is implemented in refreshBeanFactory

1.1) refreshBeanFactory

protected final void refreshBeanFactory() throws BeansException {
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
    //This method finally calls loadBeanDefinitions (EncodedResources) in the XmlBeanDefinitionReader class.
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

1.1.1) loadBean Definitions (beanFactory) (create reader)

XmlBean Definition Reader was created to read the xml configuration file.

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // 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.
   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.
   initBeanDefinitionReader(beanDefinitionReader);
   // Main logic
   loadBeanDefinitions(beanDefinitionReader);
}

1.1.1.1) loadBeanDefinitions(beanDefinitionReader)

At execution time, configResources is null and configLocations is a String []{"application Context. xml"}

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

1.1.1.1.1) XmlBean Definition Reader. loadBean Definitions (parsing tags)

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

called loadBeanDefinitions(location,null)
location is applicationContext.xml

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
   ResourceLoader resourceLoader = getResourceLoader();
   if (resourceLoader == null) {
      throw new BeanDefinitionStoreException(
            "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
   }

   if (resourceLoader instanceof ResourcePatternResolver) {
      // Resource pattern matching available.
      try {
         Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
         // Load
         int loadCount = loadBeanDefinitions(resources);
         if (actualResources != null) {
            for (Resource resource : resources) {
               actualResources.add(resource);
            }
         }
         if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
         }
         return loadCount;
      }
      catch (IOException ex) {
         throw new BeanDefinitionStoreException(
               "Could not resolve bean definition resource pattern [" + location + "]", ex);
      }
   }
   else {
      // Can only load single resources by absolute URL.
      Resource resource = resourceLoader.getResource(location);
      int loadCount = loadBeanDefinitions(resource);
      if (actualResources != null) {
         actualResources.add(resource);
      }
      if (logger.isDebugEnabled()) {
         logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
      }
      return loadCount;
   }
}

called loadBeanDefinitions(resources)
Resource is an array of elements, which is applicationContext.xml. Spring abstracts the XML file as a Resource

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

called loadBeanDefinitions(resource)

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   // Encoded Resource encapsulates resouce one more layer
   return loadBeanDefinitions(new EncodedResource(resource));
}

called loadBeanDefinitions(new EncodedResource(resource))
Here you read in the xml file and change it to inputStream

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());
   }

   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
   if (currentResources == null) {
      currentResources = new HashSet<EncodedResource>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
   }
   if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }
   try {
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
         InputSource inputSource = new InputSource(inputStream);
         if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
         }
         // Real load bean Definition
         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();
      }
   }
}

called doLoadBeanDefinitions(inputSource, encodedResource.getResource())
This is to turn inputStream into a document. Document is the result of XML DOM parsing. For more information, please see XML DOM.
Look directly at the process of converting document to bean Definitions.

doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      Document doc = doLoadDocument(inputSource, resource);
      return registerBeanDefinitions(doc, resource);
   }
   catch (BeanDefinitionStoreException ex) {
      throw ex;
   }
   ...
}

called registerBeanDefinitions(doc, resource)

registerBeanDefinitions

The task of parsing document is delegated to Bean Definition Document Reader

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;
}

called documentReader.registerBeanDefinitions(doc, createReaderContext(resource))

documentReader.registerBeanDefinitions

The implementation class is DefaultBean Definition Document Reader

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
   this.readerContext = readerContext;
   logger.debug("Loading bean definitions");
   Element root = doc.getDocumentElement();
   doRegisterBeanDefinitions(root);
}

called doRegisterBeanDefinitions

doRegisterBeanDefinitions

protected void doRegisterBeanDefinitions(Element root) {
   // Any nested <beans> elements will cause recursion in this method. In
   // order to propagate and preserve <beans> 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 = createDelegate(getReaderContext(), root, parent);

   if (this.delegate.isDefaultNamespace(root)) {
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
         if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
            if (logger.isInfoEnabled()) {
               logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                     "] not matching: " + getReaderContext().getResource());
            }
            return;
         }
      }
   }

   preProcessXml(root);
   // Core logic
   parseBeanDefinitions(root, this.delegate);
   postProcessXml(root);

   this.delegate = parent;
}

called parseBeanDefinitions(root, this.delegate)

ParseBean Definitions

The default tag is parsed separately from the custom tag. We define a component-scan tag in applicationContext.xml, and Spring treats it as a custom tag.

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)) {
               // Default label
               parseDefaultElement(ele, delegate);
            }
            else {
               // Custom Label
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      delegate.parseCustomElement(root);
   }
}

called delegate.parseCustomElement(ele)

NamespaceHandlerSupport.parse

The deletgate used here is of Namespace HandlerSupport type.

public BeanDefinition parse(Element element, ParserContext parserContext) {
   return findParserForElement(element, parserContext).parse(element, parserContext);
}

A) Find Parser ForElement (find parser)

The parser returned is the ComponentScanBean Definition Parser type.

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;
}

b) ComponentScanBeanDefinitionParser.parse

public BeanDefinition parse(Element element, ParserContext parserContext) {
   String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
   basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
   String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
         ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

   // Actually scan for bean definitions and register them.
   ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
   Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
   registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

   return null;
}

1) ClassPathBean Definition Scanner. doScan

This method searches all the class files according to the package name defined in component-scan, reads the class files, determines whether there are any annotations such as @Component, and then converts them to bean Definition. Finally, the member variable Map < beanName, beanDefinition > of beanFactory is put in. There are two key approaches: find Candidate Components and register Bean Definition. The former finds the bean Definitions corresponding to all beans that need to be managed, and the latter registers them in the BeanFactory.

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
   for (String basePackage : basePackages) {
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         if (candidate instanceof AnnotatedBeanDefinition) {
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }
         if (checkCandidate(beanName, candidate)) {
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder =
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}

1.1) findCandidateComponents

This method can be divided into three steps:
1. Get all the unfiltered class files and abstract them as Resource s
2. Read all class files
3. Judge one by one whether the bean is needed?

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
   try {
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
// Here's all the class files that were not filtered.
      Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
      boolean traceEnabled = logger.isTraceEnabled();
      boolean debugEnabled = logger.isDebugEnabled();
      for (Resource resource : resources) {
         if (traceEnabled) {
            logger.trace("Scanning " + resource);
         }
         if (resource.isReadable()) {
            try {
// The class file will be read in here.
               MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
// Determine whether the class file is a bean type registered in Spring?
               if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  if (isCandidateComponent(sbd)) {
                     if (debugEnabled) {
                        logger.debug("Identified candidate component class: " + resource);
                     }
                     candidates.add(sbd);
                  }
                  else {
                     if (debugEnabled) {
                        logger.debug("Ignored because not a concrete top-level class: " + resource);
                     }
                  }
               }
               else {
                  if (traceEnabled) {
                     logger.trace("Ignored because not matching any filter: " + resource);
                  }
               }
            }
            catch (Throwable ex) {
               throw new BeanDefinitionStoreException(
                     "Failed to read candidate component class: " + resource, ex);
            }
         }
         else {
            if (traceEnabled) {
               logger.trace("Ignored because not readable: " + resource);
            }
         }
      }
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
   }
   return candidates;
}
1.1.1) PathMatchingResourcePatternResolver.getResources
public Resource[] getResources(String locationPattern) throws IOException {
   Assert.notNull(locationPattern, "Location pattern must not be null");
   if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
      // a class path resource (multiple resources for same name possible)
      if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
         // a class path resource pattern
         return findPathMatchingResources(locationPattern);
      }
      else {
         // all class path resources with the given name
         return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
      }
   }
   else {
      // Only look for a pattern after a prefix here
      // (to not get fooled by a pattern symbol in a strange prefix).
      int prefixEnd = locationPattern.indexOf(":") + 1;
      if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
         // a file pattern
         return findPathMatchingResources(locationPattern);
      }
      else {
         // a single resource with the given name
         return new Resource[] {getResourceLoader().getResource(locationPattern)};
      }
   }
}
1.1.1.1) findPathMatchingResources
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
   String rootDirPath = determineRootDir(locationPattern);
   String subPattern = locationPattern.substring(rootDirPath.length());
// Root path, package name configured in component-scan
   Resource[] rootDirResources = getResources(rootDirPath);
   Set<Resource> result = new LinkedHashSet<Resource>(16);
   for (Resource rootDirResource : rootDirResources) {
      rootDirResource = resolveRootDirResource(rootDirResource);
      URL rootDirURL = rootDirResource.getURL();
      if (equinoxResolveMethod != null) {
         if (rootDirURL.getProtocol().startsWith("bundle")) {
            rootDirURL = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirURL);
            rootDirResource = new UrlResource(rootDirURL);
         }
      }
      if (rootDirURL.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
         result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirURL, subPattern, getPathMatcher()));
      }
      else if (ResourceUtils.isJarURL(rootDirURL) || isJarResource(rootDirResource)) {
         result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirURL, subPattern));
      }
      else {
         result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
      }
   }
   if (logger.isDebugEnabled()) {
      logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
   }
   return result.toArray(new Resource[result.size()]);
}
1.1.1.1.1) doFindPathMatchingFileResources
protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
      throws IOException {

   File rootDir;
   try {
      rootDir = rootDirResource.getFile().getAbsoluteFile();
   }
   catch (IOException ex) {
      if (logger.isWarnEnabled()) {
         logger.warn("Cannot search for matching files underneath " + rootDirResource +
               " because it does not correspond to a directory in the file system", ex);
      }
      return Collections.emptySet();
   }
   return doFindMatchingFileSystemResources(rootDir, subPattern);
}

called doFindMatchingFileSystemResources

protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
   if (logger.isDebugEnabled()) {
      logger.debug("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
   }
   // Read the file under the folder
   Set<File> matchingFiles = retrieveMatchingFiles(rootDir, subPattern);
   Set<Resource> result = new LinkedHashSet<Resource>(matchingFiles.size());
   for (File file : matchingFiles) {
      result.add(new FileSystemResource(file));
   }
   return result;
}
1.1.1.1.1.1) retrieveMatchingFiles
protected Set<File> retrieveMatchingFiles(File rootDir, String pattern) throws IOException {
   if (!rootDir.exists()) {
      // Silently skip non-existing directories.
      if (logger.isDebugEnabled()) {
         logger.debug("Skipping [" + rootDir.getAbsolutePath() + "] because it does not exist");
      }
      return Collections.emptySet();
   }
   if (!rootDir.isDirectory()) {
      // Complain louder if it exists but is no directory.
      if (logger.isWarnEnabled()) {
         logger.warn("Skipping [" + rootDir.getAbsolutePath() + "] because it does not denote a directory");
      }
      return Collections.emptySet();
   }
   if (!rootDir.canRead()) {
      if (logger.isWarnEnabled()) {
         logger.warn("Cannot search for matching files underneath directory [" + rootDir.getAbsolutePath() +
               "] because the application is not allowed to read the directory");
      }
      return Collections.emptySet();
   }
   String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/");
   if (!pattern.startsWith("/")) {
      fullPattern += "/";
   }
   fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/");
   Set<File> result = new LinkedHashSet<File>(8);
   // True logic
   doRetrieveMatchingFiles(fullPattern, rootDir, result);
   return result;
}
1.1.1.1.1.1.1.1) do Retrieve Matching Files (recursive method)
protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {
   if (logger.isDebugEnabled()) {
      logger.debug("Searching directory [" + dir.getAbsolutePath() +
            "] for files matching pattern [" + fullPattern + "]");
   }
// Get all the class files in the component-scan directory
   File[] dirContents = dir.listFiles();
   if (dirContents == null) {
      if (logger.isWarnEnabled()) {
         logger.warn("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");
      }
      return;
   }
   Arrays.sort(dirContents);
   for (File content : dirContents) {
      String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
      if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
         if (!content.canRead()) {
            if (logger.isDebugEnabled()) {
               logger.debug("Skipping subdirectory [" + dir.getAbsolutePath() +
                     "] because the application is not allowed to read the directory");
            }
         }
         else {
            doRetrieveMatchingFiles(fullPattern, content, result);
         }
      }
      if (getPathMatcher().match(fullPattern, currPath)) {
         result.add(content);
      }
   }
}
1.1.2) CachingMetadata ResourceFactory. getMetadata Reader (read class file)
public MetadataReader getMetadataReader(Resource resource) throws IOException {
   if (getCacheLimit() <= 0) {
      // True logic
      return super.getMetadataReader(resource);
   }
   synchronized (this.metadataReaderCache) {
      MetadataReader metadataReader = this.metadataReaderCache.get(resource);
      if (metadataReader == null) {
         metadataReader = super.getMetadataReader(resource);
         this.metadataReaderCache.put(resource, metadataReader);
      }
      return metadataReader;
   }
}
1.1.2.1) SimpleMetadataReader.getMetadataReader

This class encapsulates all kinds of information in the Class file and is stored in ClassMatadata and Annotation Metadata.

final class SimpleMetadataReader implements MetadataReader {

   private final Resource resource;

   private final ClassMetadata classMetadata;

   private final AnnotationMetadata annotationMetadata;
}
public MetadataReader getMetadataReader(Resource resource) throws IOException {
   return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}
SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException {
   // Open the file stream
   InputStream is = new BufferedInputStream(resource.getInputStream());
   ClassReader classReader;
   try {
      classReader = new ClassReader(is);
   }
   catch (IllegalArgumentException ex) {
      throw new NestedIOException("ASM ClassReader failed to parse class file - " +
            "probably due to a new Java class file version that isn't supported yet: " + resource, ex);
   }
   finally {
      is.close();
   }

   AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
   // Read the class file according to the class file format and save the read information to visitor
   classReader.accept(visitor, ClassReader.SKIP_DEBUG);
   // Preserved
   this.annotationMetadata = visitor;
   // (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
   this.classMetadata = visitor;
   this.resource = resource;
}
1.1.3) is Candidate Component (is it a required bean)?
 protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
   for (TypeFilter tf : this.excludeFilters) {
      if (tf.match(metadataReader, this.metadataReaderFactory)) {
         return false;
      }
   }
   for (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, this.metadataReaderFactory)) {
         return isConditionMatch(metadataReader);
      }
   }
   return false;
}

1.1.3.1) AbstractTypeHierarchyTraversingFilter.match

Focus on the matchSelf method

public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
      throws IOException {

   // This method optimizes avoiding unnecessary creation of ClassReaders
   // as well as visiting over those readers.
   if (matchSelf(metadataReader)) {
      return true;
   }
   ClassMetadata metadata = metadataReader.getClassMetadata();
   if (matchClassName(metadata.getClassName())) {
      return true;
   }

   if (this.considerInherited) {
      if (metadata.hasSuperClass()) {
         // Optimization to avoid creating ClassReader for super class.
         Boolean superClassMatch = matchSuperClass(metadata.getSuperClassName());
         if (superClassMatch != null) {
            if (superClassMatch.booleanValue()) {
               return true;
            }
         }
         else {
            // Need to read super class to determine a match...
            try {
               if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
                  return true;
               }
            }
            catch (IOException ex) {
               logger.debug("Could not read super class [" + metadata.getSuperClassName() +
                     "] of type-filtered class [" + metadata.getClassName() + "]");
            }
            }
      }
   }

   if (this.considerInterfaces) {
      for (String ifc : metadata.getInterfaceNames()) {
         // Optimization to avoid creating ClassReader for super class
         Boolean interfaceMatch = matchInterface(ifc);
         if (interfaceMatch != null) {
            if (interfaceMatch.booleanValue()) {
               return true;
            }
         }
         else {
            // Need to read interface to determine a match...
            try {
               if (match(ifc, metadataReaderFactory)) {
                  return true;
               }
            }
            catch (IOException ex) {
               logger.debug("Could not read interface [" + ifc + "] for type-filtered class [" +
                     metadata.getClassName() + "]");
            }
         }
      }
   }

   return false;
}

called matchSelf

protected boolean matchSelf(MetadataReader metadataReader) {
   AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
   return metadata.hasAnnotation(this.annotationType.getName()) ||
         (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}

this.annotationType is @Component type, so
metadata.hasAnnotation(this.annotationType.getName()) is true when @Component is annotated on the class.
Because @Service and so on also annotate @Component, so @Service, @Controller and so on are regarded as @Component here.

public boolean hasMetaAnnotation(String metaAnnotationType) {
   Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values();
   for (Set<String> metaTypes : allMetaTypes) {
      if (metaTypes.contains(metaAnnotationType)) {
         return true;
      }
   }
   return false;
}
1.2) RegiserBean Definition (record bean Definition to BeanFactory)
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
   BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
DefaultListable BeanFactory. registerBeanDefinition (save bean Definition)

After the tag is parsed, beanName and beanDefinition are put into the beanDefinition Map of the beanfactory as key s and value s.

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);
      }
   }

   BeanDefinition oldBeanDefinition;

   oldBeanDefinition = this.beanDefinitionMap.get(beanName);
   if (oldBeanDefinition != null) {
      if (!isAllowBeanDefinitionOverriding()) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
               "': There is already [" + oldBeanDefinition + "] bound.");
      }
      else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
         // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
         if (this.logger.isWarnEnabled()) {
            this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                  "' with a framework-generated bean definition: replacing [" +
                  oldBeanDefinition + "] with [" + beanDefinition + "]");
         }
      }
      else if (!beanDefinition.equals(oldBeanDefinition)) {
         if (this.logger.isInfoEnabled()) {
            this.logger.info("Overriding bean definition for bean '" + beanName +
                  "' with a different definition: replacing [" + oldBeanDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      else {
         if (this.logger.isDebugEnabled()) {
            this.logger.debug("Overriding bean definition for bean '" + beanName +
                  "' with an equivalent definition: replacing [" + oldBeanDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   else {
      if (hasBeanCreationStarted()) {
         // Cannot modify startup-time collection elements anymore (for stable iteration)
         synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);
            List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            if (this.manualSingletonNames.contains(beanName)) {
               Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
               updatedSingletons.remove(beanName);
               this.manualSingletonNames = updatedSingletons;
            }
         }
      }
      else {
         // Still in startup registration phase
         this.beanDefinitionMap.put(beanName, beanDefinition);
         this.beanDefinitionNames.add(beanName);
         this.manualSingletonNames.remove(beanName);
      }
      this.frozenBeanDefinitionNames = null;
   }

   if (oldBeanDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);
   }
}

After registration, the bean Definition Map looks like this:

2) finishBeanFactoryInitialization (bean s that initialize non-lazy-load and singleton)

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
   // Initialize conversion service for this context.
   if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
         beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
      beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
   }

   // Register a default embedded value resolver if no bean post-processor
   // (such as a PropertyPlaceholderConfigurer bean) registered any before:
   // at this point, primarily for resolution in annotation attribute values.
   if (!beanFactory.hasEmbeddedValueResolver()) {
      beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
         @Override
         public String resolveStringValue(String strVal) {
            return getEnvironment().resolvePlaceholders(strVal);
         }
      });
   }

   // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
   String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
   for (String weaverAwareName : weaverAwareNames) {
      getBean(weaverAwareName);
   }

   // Stop using the temporary ClassLoader for type matching.
   beanFactory.setTempClassLoader(null);

   // Allow for caching all bean definition metadata, not expecting further changes.
   beanFactory.freezeConfiguration();

   // Instantiate all remaining (non-lazy-init) singletons.
   // Main logic
   beanFactory.preInstantiateSingletons();
}

2.1) ConfigurableListableBeanFactory.preInstantiateSingletons

DefaultListableBeanFactory.preInstantiateSingletons
Here, the getBean method of some beans is called, that is, loading beans. That's why it was said at the beginning that the process of registering beans also included loading beans.

public void preInstantiateSingletons() throws BeansException {
   if (this.logger.isDebugEnabled()) {
      this.logger.debug("Pre-instantiating singletons in " + this);
   }

   // Iterate over a copy to allow for init methods which in turn register new bean definitions.
   // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
   List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

   // Trigger initialization of all non-lazy singleton beans...
   for (String beanName : beanNames) {
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
         if (isFactoryBean(beanName)) {
            final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
            boolean isEagerInit;
            if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
               isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                  @Override
                  public Boolean run() {
                     return ((SmartFactoryBean<?>) factory).isEagerInit();
                  }
               }, getAccessControlContext());
            }
            else {
               isEagerInit = (factory instanceof SmartFactoryBean &&
                     ((SmartFactoryBean<?>) factory).isEagerInit());
            }
            if (isEagerInit) {
               getBean(beanName);
            }
         }
         else {
            getBean(beanName);
         }
      }
   }

   // Trigger post-initialization callback for all applicable beans...
   for (String beanName : beanNames) {
      Object singletonInstance = getSingleton(beanName);
      if (singletonInstance instanceof SmartInitializingSingleton) {
         final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
         if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
               @Override
               public Object run() {
                  smartSingleton.afterSingletonsInstantiated();
                  return null;
               }
            }, getAccessControlContext());
         }
         else {
            smartSingleton.afterSingletonsInstantiated();
         }
      }
   }
}

Note that getBean s cache loaded, singleton beans, and read from the cache the next time they get beans.

Loading bean s

The loading of beans is the process of instantiating beans according to bean Definition. It can be said that getBean method is the loading of beans, and this method is very important. And the getBean method is cached. In 2) the execution process of getBean called in finishBeanFactoryInitialization is different from that of applicationContext.getBean in main method.

FactoryBean

Spring instantiates beans by specifying implementation classes using the class attribute of beans through a reflection mechanism.
Spring provides a factory-class interface for FactoryBean, through which users can customize the logic of instantiated bean s.
FactoryBean

public interface FactoryBean<T> {
// Return the bean example, and if isSingleton() returns true, the instance will be placed in the singleton cache pool in the Spring container
   T getObject() throws Exception;
   Class<?> getObjectType();
   boolean isSingleton();
}

ObjectFactory (used by Spring)

public interface ObjectFactory<T> {
   T getObject() throws BeansException;
}

Comparison:

FactoryBean:
This interface allows you to provide a complex logic to generate beans. It's essentially a Bean, but it's not used to inject into other places like Service or Dao, it's used to generate other beans. After implementing this interface, Spring takes out the beans that implement this interface when the container is initialized and generates the beans we want using the getObject() method of the interface. Of course, the business logic that generates beans also writes getObject() methods.
ObjectFactory:
It is also intended as a factory to generate Objects (this interface has only one method, getObject()). This interface is generally used to wrap a factory and return a new instance (prototype type type type) through the factory. This interface is somewhat similar to FactoryBean, but the implementation of FactoryBean is used as an instance of SPI (Service Provider Interface) in BeanFactory; the implementation of ObjectFactory is usually injected into other beans as an API. As in the case of ObjectFactory Creating FactoryBean, its return value is an ObjectFactory, which is injected into the Bean to get the desired Bean through the instance of the interface.
Generally speaking, FactoryBeans and ObjectFactory are used to obtain beans, but the methods and places used are different. After FactoryBeans are well configured, Spring calls getObject() method to get beans. After the ObjectFactory is configured, we can get ObjectFactory instances in beans. We need to call getObject() manually to get beans.

getBean

public Object getBean(String name) throws BeansException {
   return doGetBean(name, null, null, false);
}

doGetBean

Three methods are critical: getSingleton, createBean and getObjectForBean Instance.

protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {

   final String beanName = transformedBeanName(name);
   Object bean;

   // Eagerly check singleton cache for manually registered singletons.
// Check if there are corresponding instances in the cache or in the instance factory (to solve the problem of cyclic dependency)
// Spring's principle of creating beans is that the ObjectFactory that created beans will be exposed ahead of time until the beans are created, that is, the ObjectFactory will be added to the cache, and the ObjectFactory will be used directly once the next beans are created and the last beans are required.
// Direct attempts to obtain from the cache or from ObjectFactory in singleton Factories
   Object sharedInstance = getSingleton(beanName);
   if (sharedInstance != null && args == null) {
// Has been created
      if (logger.isDebugEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
    // Returns the corresponding instance (only the original state of the bean is obtained from the cache, and the bean needs to be instantiated)
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }
   else {
   // Not created, need to be created
// Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }

      // Check if bean definition exists in this factory.
      BeanFactory parentBeanFactory = getParentBeanFactory();
// If the bean DefinitionMap (loaded class) does not contain the bean Name, try processing from the parentBeanFactory
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         // Not found -> check parent.
         String nameToLookup = originalBeanName(name);
         if (args != null) {
            // Delegation to parent with explicit args.
// recursion
            return (T) parentBeanFactory.getBean(nameToLookup, args);
         }
         else {
            // No args -> delegate to standard getBean method.
            return parentBeanFactory.getBean(nameToLookup, requiredType);
         }
      }
      // Start creating bean s from here, and record them first.
      if (!typeCheckOnly) {
         markBeanAsCreated(beanName);
      }

      try {
    // Converts GenericBeanDefinition, which stores the XML configuration file, to RootBeanDefinition; when converted, if the parent bean is not empty, the properties of the parent class are merged.
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);

         // Guarantee initialization of beans that the current bean depends on. // If there are dependencies, beans that need to recursively instantiate dependencies need to be
         String[] dependsOn = mbd.getDependsOn();
         if (dependsOn != null) {
            for (String dep : dependsOn) {
               if (isDependent(beanName, dep)) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
               }
// Cache Dependent Calls
               registerDependentBean(dep, beanName);
               getBean(dep);
            }
         }

         // Create bean instance.
    // Really create bean s
         if (mbd.isSingleton()) {
    // Single case
            sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
               @Override
               public Object getObject() throws BeansException {
                  try {
                     return createBean(beanName, mbd, args);
                  }
                  catch (BeansException ex) {
                     // Explicitly remove instance from singleton cache: It might have been put there
                     // eagerly by the creation process, to allow for circular reference resolution.
                     // Also remove any beans that received a temporary reference to the bean.
                     destroySingleton(beanName);
                     throw ex;
                  }
               }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }

         else if (mbd.isPrototype()) {
            // It's a prototype -> create a new instance.
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
               afterPrototypeCreation(beanName);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }
         else {
    // Instantiate bean s on a specified scope
            String scopeName = mbd.getScope();
            final Scope scope = this.scopes.get(scopeName);
            if (scope == null) {
               throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
            }
            try {
               Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
                  @Override
                  public Object getObject() throws BeansException {
                     beforePrototypeCreation(beanName);
                     try {
                        return createBean(beanName, mbd, args);
                     }
                     finally {
                        afterPrototypeCreation(beanName);
                     }
                  }
               });
               bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
            }
            catch (IllegalStateException ex) {
               throw new BeanCreationException(beanName,
                     "Scope '" + scopeName + "' is not active for the current thread; consider " +
                     "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                     ex);
            }
         }
      }
      catch (BeansException ex) {
         cleanupAfterBeanCreationFailure(beanName);
         throw ex;
      }
   }

   // Check if required type matches the type of the actual bean instance.
   if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
      try {
         return getTypeConverter().convertIfNecessary(bean, requiredType);
      }
      catch (TypeMismatchException ex) {
         if (logger.isDebugEnabled()) {
            logger.debug("Failed to convert bean '" + name + "' to required type '" +
                  ClassUtils.getQualifiedName(requiredType) + "'", ex);
         }
         throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
      }
   }
   return (T) bean;
}

1) getSingleton (bean Name) (with caching or singleton Factories)

Logic:
1) Get from singleton Objects
2) Not retrieved from earlySingleton Objects
3) Still not, try again to get the ObjectFactory corresponding to the bean Name from the singleton Factories, then call the getObject of the ObjectFactory to create the bean, put it into earlySingleton Objects, and remove the ObjectFactory from the singleton Factories.
Membership variable Map:
1)singletonObjects: bean name-> bean instance
2)singltonFactories: bean name -> ObjectFactory
3) earlySingleton Objects: bean name - > bean instance, unlike 1), when a single bean is placed in it, then when the bean is still in the process of creation, it can be obtained through getBean, which is used to detect cyclic dependencies (the problem is explained at the end).
4) registered Singletons: Used to save all currently registered bean s.

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

2) getSingleton(beanName,ObjectFactory) (create a single bean from scratch)

Creating a single bean from scratch requires two key methods: getSingleton(beanName,ObjectFactory) and createBean.
Logic:
1) Check if the cache has been loaded
2) If not loaded, record the beanName's loading status
3) Record loading status before loading a single case
4) Instantiate bean s through ObjectFactory's Object method
5) Clear the loading state after loading a single case
6) Record the results to the cache and delete the various auxiliary states recorded during loading bean s
7) Return processing results

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "'beanName' must not be null");
   synchronized (this.singletonObjects) {
// Check whether the corresponding bean has been loaded
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
// Not loaded
         if (this.singletonsCurrentlyInDestruction) {
            throw new BeanCreationNotAllowedException(beanName,
                  "Singleton bean creation not allowed while singletons of this factory are in destruction " +
                  "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
         }
         if (logger.isDebugEnabled()) {
            logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
         }
    // Record loading status
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<Exception>();
         }
         try {
            // Initialize the bean, where the createBean method is called
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
         catch (IllegalStateException ex) {
            // Has the singleton object implicitly appeared in the meantime ->
            // if yes, proceed with it since the exception indicates that state.
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
               throw ex;
            }
         }
         catch (BeanCreationException ex) {
            if (recordSuppressedExceptions) {
               for (Exception suppressedException : this.suppressedExceptions) {
                  ex.addRelatedCause(suppressedException);
               }
            }
            throw ex;
         }
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }
            // Clear Loading State
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
            // Add cache
            addSingleton(beanName, singletonObject);
         }
      }
      return (singletonObject != NULL_OBJECT ? singletonObject : null);
   }
}
2.1) beforeSingleton Creation (record loading status)

Record the loading status, and record the beans currently being created in the cache through this. singletons CurrentlyInCreation. add (beanName), so that cyclic dependencies can be detected.

protected void beforeSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
   }
}
2.2) After Singleton Creation (Clear Load Status)

When the bean is loaded, it is necessary to remove the record of the loading state of the bean from the cache.

protected void afterSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
      throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
   }
}
2.3) addSingleton (results recorded to cache)

Record the results into the cache and delete the various auxiliary states recorded during loading bean s

protected void addSingleton(String beanName, Object singletonObject) {
   synchronized (this.singletonObjects) {
      this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
      this.singletonFactories.remove(beanName);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
   }
}

3) createBean (create a single or multiple bean, which is called in 3)

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
   if (logger.isDebugEnabled()) {
      logger.debug("Creating instance of bean '" + beanName + "'");
   }
   RootBeanDefinition mbdToUse = mbd;

   // Make sure bean class is actually resolved at this point, and
   // clone the bean definition in case of a dynamically resolved Class
   // which cannot be stored in the shared merged bean definition.
// Lock class, parse Class according to the set class attribute or according to className
   Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }

   // Prepare method overrides.
   try {
// Approaches to validation and preparation of coverage 
      mbdToUse.prepareMethodOverrides();
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
            beanName, "Validation of method overrides failed", ex);
   }

   try {
      // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// Give BeanPostProcessor a chance to return the proxy instead of the real instance
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      if (bean != null) {
         return bean;
      }
   }
   catch (Throwable ex) {
      throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
            "BeanPostProcessor before instantiation of bean failed", ex);
   }
   // Actually create bean s
   Object beanInstance = doCreateBean(beanName, mbdToUse, args);
   if (logger.isDebugEnabled()) {
      logger.debug("Finished creating instance of bean '" + beanName + "'");
   }
   return beanInstance;
}
3.1) AbstractBean Definition. prepareMethod Overrides (deciding instantiation strategy - > reflection or CGLIB)

Approaches to validation and preparation of coverage
There are lookup-method and replace-method configurations in Spring configuration. The loading of these two configurations is to store the configurations in the method Overrides attribute of BeanDefinition. The implementation principle of these two configurations is that when the method Overrides attribute is detected when the bean is instantiated, the proxy for the current bean will be generated dynamically and the corresponding blocking will be used. The truncator enhances the beans, and the related logic implementation is described in detail in the instantiation part of the beans.

public void prepareMethodOverrides() throws BeanDefinitionValidationException {
   // Check that lookup methods exists.
   MethodOverrides methodOverrides = getMethodOverrides();
   if (!methodOverrides.isEmpty()) {
      Set<MethodOverride> overrides = methodOverrides.getOverrides();
      synchronized (overrides) {
         for (MethodOverride mo : overrides) {
            prepareMethodOverride(mo);
         }
      }
   }
}
3.2) resolveBeforeInstantiation (which may create proxy bean s)

If the method returns beans that are not empty, it skips the subsequent process of actually creating beans and returns the beans directly after the proxy.
About AOP!

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
   Object bean = null;
   if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
      // Make sure bean class is actually resolved at this point.
      if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
         Class<?> targetType = determineTargetType(beanName, mbd);
         if (targetType != null) {
            bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
            if (bean != null) {
               bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
            }
         }
      }
      mbd.beforeInstantiationResolved = (bean != null);
   }
   return bean;
}
3.2.1) ApplyBeanPost Processors BeforeInstantiation (post-processor applications before instantiation)
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof InstantiationAwareBeanPostProcessor) {
         InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
         Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
         if (result != null) {
            return result;
         }
      }
   }
   return null;
}

3.2.2) ApplyBean Post Processors AfterInitialization

The rule in Spring is to ensure that the registered postProcessAfterInitialization method of the post-processor is applied to the bean as much as possible after the bean is initialized, because if the returned bean is not empty, it will not go through the normal bean creation process again.
Be careful! This may be related to AOP and Transaction.

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException {

   Object result = existingBean;
   for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
      result = beanProcessor.postProcessAfterInitialization(result, beanName);
      if (result == null) {
         return result;
      }
   }
   return result;
}

3.3) do CreateBean (create regular bean s)

Logic:
1) If it is a singleton, the cache needs to be cleared first
2) Instantiate bean s and convert BeanDefinition to BeanWrapper.
Conversion process:
(1) If there is a factory method, the factory method is used for initialization.
(2) A class has several constructors, each of which has different parameters, so the constructor needs to be locked and initialized according to the parameters.
(3) If there is neither a factory method nor a constructor with parameters, the default constructor is used to initialize the bean.
3) Application of MergedBean Definition Post Processor
Autowired annotations are used to achieve pre-parsing of types, for example, after bean s are merged.
4) Dependency Processing
A and B are singletons. B relies on A, not directly to recreate A, but to create instances by putting ObjectFactory in the cache.
5) Attribute Filling
6) Cyclic Dependency Check
7) Register Disposable Bean
If you configure destroy-method, you need to register to call it at destruction time.
8) Complete the creation and return

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
// Create new instances based on the specified bean s using corresponding policies, such as: factory method; constructor injection; simple initialization
   if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
   Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
   mbd.resolvedTargetType = beanType;

   // Allow post-processors to modify the merged bean definition.
   synchronized (mbd.postProcessingLock) {
      if (!mbd.postProcessed) {
         try {
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                  "Post-processing of merged bean definition failed", ex);
         }
         mbd.postProcessed = true;
      }
   }

   // Eagerly cache singletons to be able to resolve circular references
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
// Do you need to be exposed earlier: singletons & allow cyclic dependencies & bean s are currently being created
// Detecting cyclic dependencies
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName));
   if (earlySingletonExposure) {
      if (logger.isDebugEnabled()) {
         logger.debug("Eagerly caching bean '" + beanName +
               "' to allow for resolving potential circular references");
      }
// To avoid late cyclic dependencies, you can add the ObjectFactory that created the instance to the factory before the bean initialization is complete
      addSingletonFactory(beanName, new ObjectFactory<Object>() {
         @Override
         public Object getObject() throws BeansException {
    // Relying on references to beans again, the main application is Smart Instantiation Aware bean PostProcessor, where AOP dynamically weaves advice into beans and returns beans directly without any processing.
            return getEarlyBeanReference(beanName, mbd, bean);
         }
      });
   }
   // Fill in the bean and inject attribute values
   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
// 3.3.3
      populateBean(beanName, mbd, instanceWrapper);
      if (exposedObject != null) {
   // 3.3.4
    // Initialization methods, such as init-method, are called
         exposedObject = initializeBean(beanName, exposedObject, mbd);
      }
   }
   catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
         throw (BeanCreationException) ex;
      }
      else {
         throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
      }
   }
   // Cyclic Dependency Check
   if (earlySingletonExposure) {
      Object earlySingletonReference = getSingleton(beanName, false);
      if (earlySingletonReference != null) {
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         }
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
               if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                  actualDependentBeans.add(dependentBean);
               }
            }
            if (!actualDependentBeans.isEmpty()) {
               throw new BeanCurrentlyInCreationException(beanName,
                     "Bean with name '" + beanName + "' has been injected into other beans [" +
                     StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                     "] in its raw version as part of a circular reference, but has eventually been " +
                     "wrapped. This means that said other beans do not use the final version of the " +
                     "bean. This is often the result of over-eager type matching - consider using " +
                     "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
            }
         }
      }
   }
   // Register Disposable Bean
   // Register bean as disposable.
   try {
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}
3.3.1) CreateBean Instance (instantiated bean)

Logic:
1) If factoryMethodName property exists in RootBeanDefinition, or factory-method is configured in configuration file, Spring will try to use instantiateUsingFactoryMethod to generate instances of bean s based on configuration in RootBeanDefinition.
2) Analytical construction method and instantiation of construction method. Because there may be multiple constructors in a bean's corresponding class, and the parameters of each constructor are different, Spring then determines which constructor will be used for instantiation according to the parameters and types. However, the judgment process is a relatively performance-consuming step, so the caching mechanism, if it has been parsed, does not need to repeat the parsing but directly extracts the value from the cached value of the attribute resolvedConstructorFactoryMethod in RootBeanDefinition. Otherwise, it needs to be parsed again and the result of the parsing is added to the attribute resolvedConstructorFactor in RootBeanDefinition. YMethod.

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
   // Make sure bean class is actually resolved at this point.
// Parsing class
   Class<?> beanClass = resolveBeanClass(mbd, beanName);

   if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
   }
   // If the factory method is not empty, use the factory method to initialize the policy
   if (mbd.getFactoryMethodName() != null)  {
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }

   // Shortcut when re-creating the same bean...
   boolean resolved = false;
   boolean autowireNecessary = false;
   if (args == null) {
      synchronized (mbd.constructorArgumentLock) {
         if (mbd.resolvedConstructorOrFactoryMethod != null) {
            resolved = true;
            autowireNecessary = mbd.constructorArgumentsResolved;
         }
      }
   }
// If parsed, create it directly; otherwise, get the constructor
   if (resolved) {
      if (autowireNecessary) {
         return autowireConstructor(beanName, mbd, null, null);
      }
      else {
         return instantiateBean(beanName, mbd);
      }
   }
   // Need to analytically construct method according to parameters
   // Need to determine the constructor...
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   if (ctors != null ||
         mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
// Automatic injection of construction method
      return autowireConstructor(beanName, mbd, ctors, args);
   }
   // Use default constructors
   // No special handling: simply use no-arg constructor.
   return instantiateBean(beanName, mbd);
}

We do not configure the factory method, and we do not set up initialization using a parameterized constructor, so we will use the simplest strategy - parameterized constructor initialization.

3.3.1.1) instantiateBean (instantiation of parametric construction method)

getInstantiationStrategy() may return either direct instantiation or cglib instantiation.

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
   try {
      Object beanInstance;
      final BeanFactory parent = this;
      if (System.getSecurityManager() != null) {
         beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
               return getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
         }, getAccessControlContext());
      }
      else {
// Instance is enough.
         beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
      }
      BeanWrapper bw = new BeanWrapperImpl(beanInstance);
      initBeanWrapper(bw);
      return bw;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
   }
}
3.3.1.1.1) InstantiationStrategy.instantiate

Take Simple Instantiation Strategy as an example (reflection instantiation):

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
   // Don't override the class with CGLIB if no overrides.
// If there are methods that need to be overwritten or replaced dynamically, use cglib for dynamic proxying, because dynamic methods can be woven into classes while creating proxies, but if there are no methods that need to be changed dynamically, direct reflection will be convenient.
   if (bd.getMethodOverrides().isEmpty()) {
// There is no method that needs to be covered
      Constructor<?> constructorToUse;
      synchronized (bd.constructorArgumentLock) {
         constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
         if (constructorToUse == null) {
            final Class<?> clazz = bd.getBeanClass();
            if (clazz.isInterface()) {
               throw new BeanInstantiationException(clazz, "Specified class is an interface");
            }
            try {
               if (System.getSecurityManager() != null) {
                  constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
                     @Override
                     public Constructor<?> run() throws Exception {
                        return clazz.getDeclaredConstructor((Class[]) null);
                     }
                  });
               }
               else {
                  constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
               }
               bd.resolvedConstructorOrFactoryMethod = constructorToUse;
            }
            catch (Throwable ex) {
               throw new BeanInstantiationException(clazz, "No default constructor found", ex);
            }
         }
      }
// Reflection instantiation
      return BeanUtils.instantiateClass(constructorToUse);
   }
   else {
      // Must generate CGLIB subclass.
      return instantiateWithMethodInjection(bd, beanName, owner);
   }
}

Take Cglib Subclassing Instantiation Strategy as an example (cglib instantiation):

public Object instantiate(Constructor<?> ctor, Object... args) {
   Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
   Object instance;
   if (ctor == null) {
      instance = BeanUtils.instantiateClass(subclass);
   }
   else {
      try {
         Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
         instance = enhancedSubclassConstructor.newInstance(args);
      }
      catch (Exception ex) {
         throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
               "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
      }
   }
   // SPR-10785: set callbacks directly on the instance instead of in the
   // enhanced class (via the Enhancer) in order to avoid memory leaks.
   Factory factory = (Factory) instance;
   factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
         new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
         new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
   return instance;
}
3.3.2) getEarlyBeanReference
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
   Object exposedObject = bean;
   if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
            if (exposedObject == null) {
               return null;
            }
         }
      }
   }
   return exposedObject;
}

3.3.3) polulateBean (attribute value injection)

Logic:
1) The application of the postProcessAfterInstantiation function of the InstantiationAwareBeanPostProcessor processor, which controls whether the program continues to fill in attributes.
2) Dependent bean s are extracted according to the injection type (byName/byType) and stored uniformly in Property Values.
3) Applying the postProcessProperty Values method of InstantiationAwareBeanPostProcessor processor to reprocess attributes before they are filled in. The typical application is the validation of attributes in RequiredAnnotationBeanPostProcessor class.
4) Fill all properties in Property Values into BeanWrapper.

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
   PropertyValues pvs = mbd.getPropertyValues();

   if (bw == null) {
      if (!pvs.isEmpty()) {
         throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
      }
      else {
    // No Fillable Properties
         // Skip property population phase for null instance.
         return;
      }
   }
   // Give InstantiationAwareBeanPostProcessor the last chance to change bean s before setting properties
// For example: Types that can be used to support attribute injection
   // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
   // state of the bean before properties are set. This can be used, for example,
   // to support styles of field injection.
   boolean continueWithPropertyPopulation = true;

   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
    // Whether the return value continues to populate the bean
            if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
               continueWithPropertyPopulation = false;
               break;
            }
         }
      }
   }
   // If the post-processor issues a stop-fill command, the subsequent execution is terminated
   if (!continueWithPropertyPopulation) {
      return;
   }

   if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
         mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
      MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

      // Add property values based on autowire by name if applicable.
      if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
// Automatically inject by name and store in Property Values
         autowireByName(beanName, mbd, bw, newPvs);
      }

      // Add property values based on autowire by type if applicable.
      if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
// Automatically inject according to type and store in Property Values
         autowireByType(beanName, mbd, bw, newPvs);
      }

      pvs = newPvs;
   }
   // The post-processor has been initialized
   boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
// Dependency checks are required
   boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

   if (hasInstAwareBpps || needsDepCheck) {
      PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
      if (hasInstAwareBpps) {
// Post-processing all attributes requiring dependency checking
         for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
               InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
               pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
               if (pvs == null) {
                  return;
               }
            }
         }
      }
      if (needsDepCheck) {
         checkDependencies(beanName, mbd, filteredPds, pvs);
      }
   }
   // Applying attributes to bean s
   applyPropertyValues(beanName, mbd, bw, pvs);
}
3.3.4) initializeBean (calling init-method method method)

AbstractAutowireCapableBeanFactory
It's mainly about calling the initialization method of user settings, and there's some other work.

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
   if (System.getSecurityManager() != null) {
      AccessController.doPrivileged(new PrivilegedAction<Object>() {
         @Override
         public Object run() {
// Activating Aware Method
            invokeAwareMethods(beanName, bean);
            return null;
         }
      }, getAccessControlContext());
   }
   else {
      invokeAwareMethods(beanName, bean);
   }

   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
// Application of Post Processor
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }

   try {
// Activate user-defined init methods
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }

   if (mbd == null || !mbd.isSynthetic()) {
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}
3.3.4.1) invokeAwareMethods

Aware
Spring provides some Aware-related interfaces, such as BeanFactory Aware, Application Context Aware, etc. After the bean s that implement these Aware interfaces are initialized, some corresponding resources can be obtained.
If the bean implementing BeanFactory Aware is initialized, the Spring container will inject an instance of BeanFactory.

private void invokeAwareMethods(final String beanName, final Object bean) {
   if (bean instanceof Aware) {
      if (bean instanceof BeanNameAware) {
         ((BeanNameAware) bean).setBeanName(beanName);
      }
      if (bean instanceof BeanClassLoaderAware) {
         ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
      }
      if (bean instanceof BeanFactoryAware) {
         ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
      }
   }
}

3.3.4.2) BeanPostProcessor

The postProcessBeforeInitialization and postProcessAfterInitialization methods of BeanPostProcessor are invoked before and after user-defined initialization methods are invoked, so that users can respond to their business needs.

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
      throws BeansException {

   Object result = existingBean;
   for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
      result = beanProcessor.postProcessBeforeInitialization(result, beanName);
      if (result == null) {
         return result;
      }
   }
   return result;
}

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException {

   Object result = existingBean;
   for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
      result = beanProcessor.postProcessAfterInitialization(result, beanName);
      if (result == null) {
         return result;
      }
   }
   return result;
}
3.3.4.3) invokeInitMethods (activating custom init methods)

In addition to configuring init-method, the initialization method of customization also implements Initializing bean interface for customized beans. It also implements its own initialization business logic in the afterProperties Set.
Both init-method and afterPropertiesSet are executed when the bean is initialized. The sequence of execution is afterPropertiesSet executed first and init-method executed later.
Initialization method calls of these two steps are implemented in this method.

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
      throws Throwable {
   // First check whether it is an InitializingBean, and if so, call the afterPropertiesSet method?
   boolean isInitializingBean = (bean instanceof InitializingBean);
   if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
      if (logger.isDebugEnabled()) {
         logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
      }
      if (System.getSecurityManager() != null) {
         try {
            AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
               @Override
               public Object run() throws Exception {
                  ((InitializingBean) bean).afterPropertiesSet();
                  return null;
               }
            }, getAccessControlContext());
         }
         catch (PrivilegedActionException pae) {
            throw pae.getException();
         }
      }
      else {
         ((InitializingBean) bean).afterPropertiesSet();
      }
   }

   if (mbd != null) {
      String initMethodName = mbd.getInitMethodName();
      if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
            !mbd.isExternallyManagedInitMethod(initMethodName)) {
         invokeCustomInitMethod(beanName, bean, mbd);
      }
   }
}
3.3.5) getSingleton(beanName,allowEarlyReference)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
3.3.6) Register Disposable Bean If Necessary (Register Disposable Bean)

In addition to configuring the attribute destroy-method, users can also register the post-processor Destruction AwareBeanPostProcessor to unify the destruction methods of bean s.

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
   AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
   if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
      if (mbd.isSingleton()) {
    // In the singleton mode, registering beans that need to be destroyed handles beans that implement Disposable Beans and uses Destruction Aware bean Post Processors for all beans
         // Register a DisposableBean implementation that performs all destruction
         // work for the given bean: DestructionAwareBeanPostProcessors,
         // DisposableBean interface, custom destroy method.
         registerDisposableBean(beanName,
               new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
      }
      else {
// Processing of custom scope
         // A bean with a custom scope...
         Scope scope = this.scopes.get(mbd.getScope());
         if (scope == null) {
            throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
         }
         scope.registerDestructionCallback(beanName,
               new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
      }
   }
}
4) getObjectForBeanInstance (getting objects from instances of bean s)

Whether beans are retrieved from caches or loaded through different scope policies, they are only the primitive state of beans, not necessarily the beans we ultimately want.
For example, we need to process FactoryBeans, so what we get here is actually the initial state of FactoryBeans, but what we really need is the bean s returned from the factory-method (getObject method) method defined in FactoryBeans, and getObjectForBeanInstance does that.

This method completes the following tasks:
1) Verification of FactoryBean's Correctness
2) Do not deal with non-FactoryBean s
3) Conversion of bean s
4) Delegate the work of parsing bean s from Factory to getObjectFromFactoryBean.
Because we did not implement the FactroyBean interface, we returned directly.

protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {

   // Don't let calling code try to dereference the factory if the bean isn't a factory.
   if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
      throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
   }

   // Now we have the bean instance, which may be a normal bean or a FactoryBean.
   // If it's a FactoryBean, we use it to create a bean instance, unless the
   // caller actually wants a reference to the factory.
   if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
      return beanInstance;
   }

   Object object = null;
   if (mbd == null) {
      object = getCachedObjectForFactoryBean(beanName);
   }
   if (object == null) {
      // Return bean instance from factory.
      FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
      // Caches object obtained from FactoryBean if it is a singleton.
      if (mbd == null && containsBeanDefinition(beanName)) {
         mbd = getMergedLocalBeanDefinition(beanName);
      }
      boolean synthetic = (mbd != null && mbd.isSynthetic());
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
   }
   return object;
}
4.1.1) doGetObjectFromFactoryBean
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
      throws BeanCreationException {

   Object object;
   try {
      if (System.getSecurityManager() != null) {
         AccessControlContext acc = getAccessControlContext();
         try {
            object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
               @Override
               public Object run() throws Exception {
                     return factory.getObject();
                  }
               }, acc);
         }
         catch (PrivilegedActionException pae) {
            throw pae.getException();
         }
      }
      else {
         object = factory.getObject();
      }
   }
   catch (FactoryBeanNotInitializedException ex) {
      throw new BeanCurrentlyInCreationException(beanName, ex.toString());
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
   }

   // Do not accept a null value for a FactoryBean that's not fully
   // initialized yet: Many FactoryBeans just return null then.
   if (object == null && isSingletonCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(
            beanName, "FactoryBean which is currently in creation returned null from getObject");
   }
   return object;
}

Cyclic dependency

Class A holds class B references, class B holds class C references, and class C holds class A references, forming circular dependencies.
Spring container cyclic dependencies include constructor cyclic dependencies and setter cyclic dependencies.

Constructor cyclic dependency

Represents a cyclic dependency constructed by constructor injection. This dependency cannot be resolved. Only BeanCurrentlyInCreationException exceptions can be thrown to represent cyclic dependencies.
For example, when creating CircleA class, the constructor needs CircleB class, which will create CircleB. When creating CircleB class, it finds that CircleC class is needed, then it creates CircleC. Finally, when creating CircleC, it finds that CircleA is needed; thus forming a ring, it can not be created.
The Spring container places each bean identifier being created in a "Currently Created Bean Pool" and the bean identifier will remain in the pool during the creation process, so if you find yourself in the "Currently Created Bean Pool" during the creation process, you will throw a Bean CurrentlyInCreation Exception exception to indicate circular dependency; for beans that have been created, you will start from Clear out the Current Create Bean Pool.

setter cyclic dependency

Represents a cyclic dependency formed by setter injection.
The dependency on setter injection is achieved by exposing the beans that have just completed the constructor injection but have not completed other steps (such as setter injection) in advance by the Spring container, and can only solve the Bean cyclic dependency of the singleton scope.
1. Spring containers create a single "circleA" Bean. First, beans are created according to a parametric constructor, and an "ObjectFactory" is exposed to return a bean that is created in advance, and the "circleA" identifier is placed in the "Current Created Bean Pool"; then setter is injected into "circleB";
2. Spring container creates a single "circleB" Bean. First, it creates a Bean according to the parametric constructor, and exposes an "ObjectFactory" to return a bean that exposes a creation ahead of time. Then it puts the "circleB" identifier into the "Current Created Bean Pool" and injects "circleC" into the setter.
3. Spring container creates a single "circleC" Bean. First, it creates a Bean according to the non-parametric constructor, and exposes an "ObjectFactory" to return a bean in advance, and puts the "circleC" identifier into the "Current Created Bean Pool", then sets it into "circleA"; when injecting "circleA", it exposes the "ObjectFactory" beforehand. ” The factory then uses it to return to expose a bean in advance;
4. Finally, the setter injection is completed in dependency injection of "circleB" and "circleA".

For "prototype" scoped beans, the Spring container cannot complete dependency injection, because the "prototype" scoped beans, the Spring container does not cache, so it cannot expose a Bean in advance.

summary

A flow chart can be used to explain the registration of the whole bean and the loading process of the bean. These two examples are from the network, intrusion and deletion.

Registration of bean s

Loading bean s

know

  1. The code in Spring can be said to be very complex, but also very rigorous. Almost every method has Assert, check in parameters, throw exceptions and so on, and handle exceptions. Often most of the code in a method is security checking, and finally a doXXX method is called to execute the core logic. This is worth learning.
  2. Template method patterns are widely used in Spring, leaving room for subclass extensions when defining Abstract parent classes. Complex inheritance architecture achieves the maximum possible reusability and extensibility of code.
  3. The use of a large number of caches. It can be said that a large number of resources can be cached, Spring's caching mechanism in exchange for performance improvements.
  4. The principle of single responsibility. Spring code has a lot of delegation of responsibility, which is not very close to the current class business relationship to other classes to achieve, so as to avoid writing a class very inflated. Although there are many classes and methods in Spring, there are few classes or methods with very large amount of code. Most of them decompose business logic to ensure that at least a single method is readable.

Experience

  1. There are two ways to read the source code. One is to read the source code directly, such as Spring Source Deep Parsing, and the other is to write an example and debug the code. The former may have comparative headaches, while the latter may ignore other execution processes. It's best to combine the two.
  2. Instead of understanding everything, focus on the core logic and try to combine the source code with what you've touched and used, such as IOC, which starts with XML or annotations and ends up with reflection or CGLIB.
  3. My article may have overlooked some parts, such as BeanFactory inheritance system, Resource inheritance system and so on, which can search the corresponding information on the Internet.

Topics: Spring xml Attribute Java