Spring IOC- Container based on XML configuration

We first analyze the AbstractXmlApplicationContext this container loading process.

Dad is AbstractRefreshableApplicationContext AbstractXmlApplicationContext.

It was dad AbstractApplicationContext of one of the two sons.

1. Start

Start with a diagram:

Using the ClassPathXmlApplicationContext demo, and then there’s brother FileSystemXmlApplicationContext next to it, both of them are from the XML configuration file loading configuration

There is also a AnnotationConfigApplicationContext besides these two

2. Source code analysis

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
  // Array of configuration files
  private Resource[] configResources;

  // Specify the parent container of ApplicationContext
  public ClassPathXmlApplicationContext(ApplicationContext parent) {
    super(parent);
  }
  
  public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
  		this(new String[] {configLocation}, true.null);
  }
  
  public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
  		this(configLocations, true.null);
  }
  
  public ClassPathXmlApplicationContext(String[] configLocations, @Nullable ApplicationContext parent)
  			throws BeansException {
  
  		this(configLocations, true, parent);
  }
  
  public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
      throws BeansException {

    super(parent);
    // According to the path provided, processing into an array of configuration files (semicolons, commas, Spaces, tabs, newlines)
    setConfigLocations(configLocations);
    
    if(refresh) { refresh(); }}}Copy the code

As you can see, the ClassPathXmlApplicationContext provides a series of construction method, main is to specify the location of the configuration files, and to set a parent container, and call the refresh () method, setConfigLocations () this is very simple, The main one, it seems, is in the Refresh method

setConfigLocations()

So what’s going on in the setConfigLocations() method

public void setConfigLocations(String... locations) {
    if(locations ! =null) {
        Assert.noNullElements(locations, "Config locations must not be null");
        this.configLocations = new String[locations.length];
        for (int i = 0; i < locations.length; i++) {
            / / to look down
            this.configLocations[i] = resolvePath(locations[i]).trim(); }}else {
        this.configLocations = null; }}protected String resolvePath(String path) {
    return getEnironment().resolveRequiredPlaceholders(path);
}
Copy the code

The setConfigLocations() method does two main things: Create environment object ConfigurableEnvironment and deal with the incoming string in the ClassPathXmlApplicationContext structure of placeholder, getEnvironment here () goes to create environment variables related to the operation

GetEnvironment () Obtains environment variables

public ConfigurableEnvironment getEnvironment(a) {
    if (this.environment == null) {
        this.environment = createEnvironment();
    }
    return this.environment;
}
Copy the code

In the getEnvironment() method above, we see a ConfigurableEnvironment class:

There are two important parts of this interface. One is to set up the Spring environment, which is the Spring. Profile configuration we often use. The other is system resource Property

Then look at the createEnvironment() method in getEnvironment(), which creates a StandardEnvironment:

public class StandardEnvironment extends AbstractEnvironment {

	/** System environment property source name: {@value} System properties eg: system.getProperty ("java.home"); * /
	public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";

	/** JVM system properties property source name: {@value} JVM attribute --name=ranger OR -Dname= Ranger */
	public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";


	/** * Customize the set of property sources with those appropriate for any standard * Java environment: * Define the resource set using any suitable standard Java environment * <ul> * <li>{@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME}
	 * <li>{@value#SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME} * </ul> * JVM Properties have higher priority than system Properties * <p>Properties present in {@value #SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME} will
	 * take precedence over those in {@value #SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME}.
	 * @seeAbstractEnvironment#customizePropertySources(MutablePropertySources) * According to the parent class definition of this method: AbstractEnvironment#customizePropertySources Subclasses can override this method to add a custom PropertySource as follows: Public Class Level1Environment extends AbstractEnvironment {&#064; Override protected void customizePropertySources(MutablePropertySources propertySources) { super.customizePropertySources(propertySources); // no-op from base class propertySources.addLast(new PropertySourceA(...) ); propertySources.addLast(new PropertySourceB(...) ); }} You can also add A custom PropertySource within A Level1Environment subclass, and the PropertySource has A priority, such as > A > B * in the above example@see #getSystemProperties()
	 * @see #getSystemEnvironment()
	 */
	@Override
	protected void customizePropertySources(MutablePropertySources propertySources) {
		propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
		propertySources.addLast(newSystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment())); }}Copy the code

The customizePropertySources method in this class adds Java process variables and system environment variables to the resource list. It can be seen that an Environment is a collection of resources containing a set of PropertySource, which is the property of the resource

Once again return to resolvePath () method, has been tracking this method, finally to the org. Springframework. Util. PropertyPlaceholderHelper. ParseStringValue () method, This method basically handles all placeholders that use ${}

protected String parseStringValue( String value, PlaceholderResolver placeholderResolver, Set
       
         visitedPlaceholders)
        {

      StringBuilder result = new StringBuilder(value);
      int startIndex = value.indexOf(this.placeholderPrefix);
      while(startIndex ! = -1) {
         int endIndex = findPlaceholderEndIndex(result, startIndex);
         if(endIndex ! = -1) {
            String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
            String originalPlaceholder = placeholder;
            if(! visitedPlaceholders.add(originalPlaceholder)) {throw new IllegalArgumentException(
                     "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
            }
            placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
            // Now obtain the value for the fully resolved key...
            String propVal = placeholderResolver.resolvePlaceholder(placeholder);
            if (propVal == null && this.valueSeparator ! =null) {
               int separatorIndex = placeholder.indexOf(this.valueSeparator);
               if(separatorIndex ! = -1) {
                  String actualPlaceholder = placeholder.substring(0, separatorIndex);
                  String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
                  propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                  if (propVal == null) { propVal = defaultValue; }}}if(propVal ! =null) {
               // Recursive invocation, parsing placeholders contained in the
               // previously resolved placeholder value.
               propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
               result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
               if (logger.isTraceEnabled()) {
                  logger.trace("Resolved placeholder '" + placeholder + "'");
               }
               startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
            }
            else if (this.ignoreUnresolvablePlaceholders) {
               // Proceed with unprocessed value.
               startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
            }
            else {
               throw new IllegalArgumentException("Could not resolve placeholder '" +
                     placeholder + "'" + " in value \"" + value + "\" ");
            }
            visitedPlaceholders.remove(originalPlaceholder);
         }
         else {
            startIndex = -1; }}return result.toString();
   }
Copy the code

The PropertyResolver will be resolved later

refresh()

This method is very long, and will be introduced in a long way. Let’s look at what it does:

public void refresh(a) throws BeansException, IllegalStateException {
      synchronized (this.startupShutdownMonitor) {
         // Prepare this context for refreshing.
         prepareRefresh();

         // Tell the subclass to refresh the internal bean factory.
         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

         // Prepare the bean factory for use in this context.
         prepareBeanFactory(beanFactory);

         try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            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(); }}}Copy the code

Next step by step:

  1. Synchronized in order to avoid the conflict caused by restarting containers before they are started in multi-threaded environment
  2. The prepareRefresh() method does some preparatory work, recording the container’s startup time, marking up the startup status, and so on
protected void prepareRefresh(a) {
      this.startupDate = System.currentTimeMillis();
      this.closed.set(false);
      this.active.set(true);

      if (logger.isInfoEnabled()) {
         logger.info("Refreshing " + this);
      }

      // Initialize the load configuration file method, which is not implemented specifically, an extension point left to the user
      initPropertySources();

      // Check environment variables
      getEnvironment().validateRequiredProperties();

      this.earlyApplicationEvents = new LinkedHashSet<>();
   }
Copy the code

One method for validateRequiredProperties (), the method is to check whether all the required environment variables is empty, if is empty, stop start, and then throw an exception

  1. The obtainFreshBeanFactory() method is responsible for the BeanFactory initialization, Bean loading, and registration events
protected ConfigurableListableBeanFactory obtainFreshBeanFactory(a) {
   / / core
   refreshBeanFactory();

   // Returns the created BeanFactory
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ":" + beanFactory);
   }
   return beanFactory;
}
Copy the code
  • refreshBeanFactory():
/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. */
@Override
protected final void refreshBeanFactory(a) throws BeansException {
    // Determine if the current ApplicationContext has a BeanFactory. If so, destroy all beans and close the BeanFactory
    // Note that an application can have more than one BeanFactory. This is to determine whether the current ApplicationContext has a BeanFactory
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
         / / initialize DefaultListableBeanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        // Set two configuration properties of BeanFactory: whether Bean overrides are allowed and whether circular references are allowed
        customizeBeanFactory(beanFactory);
        // Load the Bean into the BeanFactory
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory; }}catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for "+ getDisplayName(), ex); }}Copy the code

Initialize a DefaultListableBeanFactory here, look at this class inheritance

This is a very big guy

  • loadBeanDefinitions()

BeanDefinition first, we know that a BeanFactory is a Bean container, and a BeanDefinition is a form of Bean (it contains properties related to the class the Bean points to, whether it is a singleton, whether it is lazy to load, Bean dependencies, etc.). BeanFactory is the BeanDefinition that’s saved.

BeanDefinition interface definition:

public interface BeanDefinition extends AttributeAccessor.BeanMetadataElement {

   // The Bean lifecycle is provided in sington and prototype by default. In WebApplicationContext there are request, Session, globalSession, Application, WebSocket, etc
   String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
   String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

  
   // Set the parent Bean
   void setParentName(String parentName);

   // Get the parent Bean
   String getParentName(a);

   // Set the class name of the Bean
   void setBeanClassName(String beanClassName);

   // Get the class name of the Bean
   String getBeanClassName(a);


   // Set the scope of the bean
   void setScope(String scope);

   String getScope(a);

   // Set lazy loading
   void setLazyInit(boolean lazyInit);

   boolean isLazyInit(a);

   // Set all the beans that the Bean depends on
   void setDependsOn(String... dependsOn);

   // Returns all dependencies for the Bean
   String[] getDependsOn();

   // Sets whether this Bean can be injected into other beans
   void setAutowireCandidate(boolean autowireCandidate);

   // Whether this Bean can be injected into other beans
   boolean isAutowireCandidate(a);

   // Multiple implementations of the same interface. If no name is specified, Spring will preferentially select beans with primary set to true
   void setPrimary(boolean primary);

   // Is primary
   boolean isPrimary(a);

   // Specify the factory name
   void setFactoryBeanName(String factoryBeanName);
   // Get the factory name
   String getFactoryBeanName(a);
   // Specify the name of the factory method in the factory class
   void setFactoryMethodName(String factoryMethodName);
   // Get the factory method name in the factory class
   String getFactoryMethodName(a);

   // Constructor arguments
   ConstructorArgumentValues getConstructorArgumentValues(a);

   // The property value in the Bean, which will be described later when injecting the property value into the Bean
   MutablePropertyValues getPropertyValues(a);

   / / if the singleton
   boolean isSingleton(a);

   / / whether the prototype
   boolean isPrototype(a);

   // If the Bean is set to abstract, it cannot be instantiated and is often used as a parent Bean for inheritance
   boolean isAbstract(a);

   int getRole(a);
   String getDescription(a);
   String getResourceDescription(a);
   BeanDefinition getOriginatingBeanDefinition(a);
}
Copy the code

The loadBeanDefinitions() method reads the configuration file and loads each BeanDefinition into the BeanFactory

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Instantiate XmlBeanDefinitionReader
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // Initialize BeanDefinitionReader
   initBeanDefinitionReader(beanDefinitionReader);
   // Continue reading
   loadBeanDefinitions(beanDefinitionReader);
}

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

Then call the loadBeanDefinitions() method of XmlBeanDefinitionReader

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
   Assert.notNull(resources, "Resource array must not be null");
   int counter = 0;
   // Process all configuration files
   for (Resource resource : resources) {
      // Keep reading
      counter += loadBeanDefinitions(resource);
   }
   // Finally returns the number of all beanDefinitions loaded
   return counter;
}

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


public int loadBeanDefinitions(String location, @Nullable 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) {
     try {
       // Convert the configuration file to a Resource object
        Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
       // Continue reading
        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 + "]");
     }
     returnloadCount; }}Copy the code

XmlBeanDefinitionReader is mentioned several times. The main interface for this class is BeanDefinitionReader

How does this class read a configuration file, create a beanDefinitionMap, and then register it with the container beanDefinitionMap, which we’ll talk about later, if we look too far, we’ll never get back

  1. prepareBeanFactory(ConfigurableListableBeanFactory beanFactory)

Now back to the main thread of the refresh() method, and now refresh has gone to prepare Something for the BeanFactory, which, by its name, is actually the classloader for the BeanFactory, Add BeanPostProcessor, and then manually register a few special beans

/**
 * Configure the factory's standard context characteristics,
 * such as the context's ClassLoader and post-processors.
 * @param beanFactory the BeanFactory to configure
 */
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // Tell the internal bean factory to use the context's class loader etc.
    // Set the classloader for the current BeanFacoty
    beanFactory.setBeanClassLoader(getClassLoader());
    / / set BeanExpressionResolver
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    / / register PropertyEditor
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // Configure the bean factory with context callbacks.
    // This processor is responsible for the callback when all beans that implement the Aware interface are initialized
    // For example,bean retrieves ApplicationContext and Implement ApplicationContextAware
    // Of course, it also handles beans that implement EnvironmentAware, ResourceLoaderAware, and so on
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    
    // The following lines mean that if a bean depends on the implementation class of any of the following interfaces, ignore them during autowiring
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    // The following lines assign values to specific beans. If any beans depend on the following, the corresponding values will be injected
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // Register early post-processor for detecting inner beans as ApplicationListeners.
    // Register event listeners
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

    // Detect a LoadTimeWeaver and prepare for weaving, if found.
    // Register a BeanPostProcessor if there is a bean named loadTimeWeaver
    // LoadTimeWeaver is a LoadTimeWeaver
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // Set a temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // Register default environment beans.
    // Register the environment bean
    if(! beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment()); }// System properties
    if(! beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) { beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME,  getEnvironment().getSystemProperties()); }/ / the JVM properties
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}
Copy the code
  1. postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

This is an extension provided in Spring that calls the class and leaves it up to the subclasses to extend, and the subclasses can add some special BeanFactoryPostProcessor implementation classes or do something at this point. The method definition is as follows

/**
 * Modify the application context's internal bean factory after its standard
 * initialization. All bean definitions will have been loaded, but no beans
 * will have been instantiated yet. This allows for registering special
 * BeanPostProcessors etc in certain ApplicationContext implementations.
 * @param beanFactory the bean factory used by the application context
 */
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}Copy the code
  1. invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)

Here is the offer of an extension Spring, if you have the bean implementation Spring BeanFactoryPostProcessor, so the container after initialization, Spring will be responsible for the inside of the call postProcessBeanFactory method of this interface are defined as follows:

public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanfactory) throws BeansException;
}
Copy the code
  1. registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory);

Here, again, is an extension point that registers the bean handlers that intercept bean creation. Each bean creation calls all beanPostProcessors to register the implementation classes of BeanPostProcessor. BeanFactoryPostProcessor This interface has two methods: PostProcessBeforeInitialization and postProcessAfterInitialization respectively in Bean initialization before and after the initialization enforced

  1. initMessageSource()

Initializes the MessageSource of the current ApplicationContext for internationalization

  1. initApplicationEventMulticaster()

Initialize the current radio apparatus, the BeanFactory events will register a SimpleApplicationEventMulticaster singleton, can call ApplicationEventMulticaster multicastEvent ways to trigger an event, ApplicationEventMulticaster invokes the corresponding ApplicationListener, behind to speak Spring event mechanism in detail again

  1. onRefresh()

Here’s another extension point for subclasses,

/**
 * Template method which can be overridden to add context-specific refresh work.
 * Called on initialization of special beans, before instantiation of singletons.
 * <p>This implementation is empty.
 * @throws BeansException in case of errors
 * @see #refresh()
 */
protected void onRefresh(a) throws BeansException {
    // For subclasses: do nothing by default.
}
Copy the code
  1. registerListeners();

Detect the Listener and register with the container

protected void registerListeners(a) {
  // Add some listeners for manual set first
  for(ApplicationListener<? > listener : getApplicationListeners()) { getApplicationEventMulticaster().addApplicationListener(listener); }// Get the name of the listener and set it to the broadcaster
  String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true.false);
  for (String listenerBeanName : listenerBeanNames) {
     getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
  }

  // Publish if there are early application events
  Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
  this.earlyApplicationEvents = null;
  if(earlyEventsToProcess ! =null) {
     for(ApplicationEvent earlyEvent : earlyEventsToProcess) { getApplicationEventMulticaster().multicastEvent(earlyEvent); }}}Copy the code
  1. finishBeanFactoryInitialization()

Having gone a long way in the refresh method before, but still not completing the bean initialization and dependency injection, Spring feels it’s time to initialize the singleton bean that isn’t lazily loaded

It’s going to be a tough ride here again

/** * Finish the initialization of this context's bean factory, * initializing all remaining singleton beans. */
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // Initialize conversion service for this context.
    // Initialize ConversionService for the current BeanFactory
    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(strVal -> 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.  
    beanFactory.preInstantiateSingletons();
}

Copy the code
  • ConversionService this class is related to type conversion. One of the scenarios is to convert parameters passed from the front end to the controller method format

  • EmbeddedValueResolver The EmbeddedValueResolver provides an easy way to read configuration file properties

@Component
public class PropertiesUtil implements EmbeddedValueResolverAware {

    private StringValueResolver resolver;

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.resolver = resolver;
    }


    /** * get the attribute by passing the attribute name */
    public String getPropertiesValue(String key) {
        StringBuilder name = new StringBuilder("${").append(key).append("}");
        returnresolver.resolveStringValue(name.toString()); }}Copy the code
  • beanFactory.preInstantiateSingletons()

This method is important

public void preInstantiateSingletons(a) throws BeansException {
   if (this.logger.isDebugEnabled()) {
      this.logger.debug("Pre-instantiating singletons in " + this);
   }
   // this.beandefinitionNames saves all beanNames
   List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

   for (String beanName : beanNames) {
      // Return a merged RootBeanDefinition, traversing the parent bean definition,if the specified bean corresponds to a child bean definition.
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

      // Not abstract, singleton, and not lazily loaded
      if(! bd.isAbstract() && bd.isSingleton() && ! bd.isLazyInit()) {/ / processing FactoryBean
         if (isFactoryBean(beanName)) {
            // Prefix beanName with an ampersand
            finalFactoryBean<? > factory = (FactoryBean<? >) getBean(FACTORY_BEAN_PREFIX + beanName);// Determine if the current FactoryBean is an implementation of SmartFactoryBean
            boolean isEagerInit;
            if(System.getSecurityManager() ! =null && factory instanceof SmartFactoryBean) {
               isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                  @Override
                  public Boolean run(a) {
                     return((SmartFactoryBean<? >) factory).isEagerInit(); } }, getAccessControlContext()); }else {
               isEagerInit = (factory instanceofSmartFactoryBean && ((SmartFactoryBean<? >) factory).isEagerInit()); }if(isEagerInit) { getBean(beanName); }}else {
            // Non-factoryBeans are initialized directly using this methodgetBean(beanName); }}}/ / if the bean implementation SmartInitializingSingleton interface, so get back here
   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(a) {
                  smartSingleton.afterSingletonsInstantiated();
                  return null;
               }
            }, getAccessControlContext());
         }
         else{ smartSingleton.afterSingletonsInstantiated(); }}}}Copy the code

As you can see, the getBean(String beanName) method is finally called, whether or not it is a FactoryBean

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

protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {
   // Get beanName: A FactoryBean(preceded by an '&') and a beanName can be converted to the most orthodox beanName
  // The main logic is that if it is a FactoryBean, the & is removed. If it is an alias, the real name is obtained from the alias
   final String beanName = transformedBeanName(name);

   // The last return value
   Object bean;

   // Check to see if it is initialized
   Object sharedInstance = getSingleton(beanName);
  // If it is already initialized and no args is passed, it means get
   if(sharedInstance ! =null && args == null) {
      if (logger.isDebugEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("...");
         }
         else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); }}// If it is a normal Bean, it returns the instance object it created
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   else {
      // If this bean of type prototype exists
      if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
      }

      // If the current BeanDefinition does not exist and has a parent BeanFactory
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
         String nameToLookup = originalBeanName(name);
      // Returns the query result of the parent container
         if(args ! =null) {
            return (T) parentBeanFactory.getBean(nameToLookup, args);
         }
         else {
            returnparentBeanFactory.getBean(nameToLookup, requiredType); }}if(! typeCheckOnly) {// typeCheckOnly is false to put the current beanName into an alreadyCreated Set.
         markBeanAsCreated(beanName);
      }

      /* * to create the bean */
      try {
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);

         // Initialize all the beans of the dependency, as defined in Depends -on
         String[] dependsOn = mbd.getDependsOn();
         if(dependsOn ! =null) {
            for (String dep : dependsOn) {
               // Check if there are loop dependencies
               if (isDependent(beanName, dep)) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
               }
               // Register dependencies
               registerDependentBean(dep, beanName);
               // Initialize the dependents firstgetBean(dep); }}// if it is a singleton
         if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
               @Override
               public Object getObject(a) throws BeansException {
                  try {
                     // Perform the create Bean, as described below
                     return createBean(beanName, mbd, args);
                  }
                  catch (BeansException ex) {
                     destroySingleton(beanName);
                     throwex; }}}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }// 如果是prototype
         else if (mbd.isPrototype()) {
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               // Perform the create Bean
               prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
               afterPrototypeCreation(beanName);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }

         // If it is not singleton and Prototype, then it is a custom scope, such as session in the Web project, which is left to the application of the custom scope
         else {
            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(a) throws BeansException {
                     beforePrototypeCreation(beanName);
                     try {
                        // Perform the create Bean
                        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);
         throwex; }}// Check the type of bean
   if(requiredType ! =null&& bean ! =null && !requiredType.isInstance(bean)) {
      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 newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
}

Copy the code

The bean is checked to see if it exists and returned if it does. If the createBean(String beanName, RootBeanDefinition MBD, Object[] args) method is not called

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 the Class in BeanDefinition is loadedClass<? > resolvedClass = resolveBeanClass(mbd, beanName);if(resolvedClass ! =null&&! mbd.hasBeanClass() && mbd.getBeanClassName() ! =null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }

   // Prepare method overwrites if < look-method /> and < rel-method /> are defined in the bean.
   try {
      mbdToUse.prepareMethodOverrides();
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
            beanName, "Validation of method overrides failed", ex);
   }

   try {
      // Return if there is a proxy
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      if(bean ! =null) {
         returnbean; }}catch (Throwable ex) {
      throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
            "BeanPostProcessor before instantiation of bean failed", ex);
   }
   / / create a bean
   Object beanInstance = doCreateBean(beanName, mbdToUse, args);
   if (logger.isDebugEnabled()) {
      logger.debug("Finished creating instance of bean '" + beanName + "'");
   }
   return beanInstance;
}

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

   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
    // If it is. FactoryBean, it is removed from the cache
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      // Instantiate the Bean. This method is the endpoint
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   / / the bean instance
   finalObject bean = (instanceWrapper ! =null ? instanceWrapper.getWrappedInstance() : null);
   / / bean typeClass<? > beanType = (instanceWrapper ! =null ? instanceWrapper.getWrappedClass() : null);
   mbd.resolvedTargetType = beanType;

   synchronized (mbd.postProcessingLock) {
      if(! mbd.postProcessed) {try {
            / / cycle call implements MergedBeanDefinitionPostProcessor postProcessMergedBeanDefinition method of interface
         Spring has several default implementations of this interface, the most familiar of which operates on the @autowired annotation
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                  "Post-processing of merged bean definition failed", ex);
         }
         mbd.postProcessed = true; }}// Resolve loop 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");
      }
    // When A is being created, A depends on B, so early expose is made by (8) putting A into the singleton factory as an ObjectFactory, where B needs to reference A, but A is being created, and gets the ObjectFactory from the singleton factory, thus allowing cyclic dependency
      addSingletonFactory(beanName, new ObjectFactory<Object>() {
         @Override
         public Object getObject(a) throws BeansException {
            returngetEarlyBeanReference(beanName, mbd, bean); }}); } Object exposedObject = bean;try {
      // Responsible for attribute assembly, very important, below
      // Assemble attributes
      populateBean(beanName, mbd, instanceWrapper);
      if(exposedObject ! =null) {
         // Handle the various callbacks after the bean is initialized, such as init-method, InitializingBean interface, BeanPostProcessor interfaceexposedObject = 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); }}// Similarly, if there are cyclic dependencies
   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 the bean with the appropriate Scope
   try {
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}

Copy the code
  • populateBean((String beanName, RootBeanDefinition mbd, BeanWrapper bw)

This method completes the injection of properties inside the bean

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
   // All attributes of the bean
   PropertyValues pvs = mbd.getPropertyValues();

   if (bw == null) {
      if(! pvs.isEmpty()) {throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
      }
      else {
         return; }}boolean continueWithPropertyPopulation = true;
   if(! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            // If false is returned, no further attribute setting is required and no further BeanPostProcessor processing is required
            if(! ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { continueWithPropertyPopulation =false;
               break; }}}}if(! continueWithPropertyPopulation) {return;
   }

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

      // Find all attribute values by name. If it is a bean dependency, initialize the dependent bean first. Recording dependencies
      if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
         autowireByName(beanName, mbd, bw, newPvs);
      }

      // Assemble by type. complicated
      if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
         autowireByType(beanName, mbd, bw, newPvs);
      }

      pvs = newPvs;
   }

   boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
   booleanneedsDepCheck = (mbd.getDependencyCheck() ! = RootBeanDefinition.DEPENDENCY_CHECK_NONE);if(hasInstAwareBpps || needsDepCheck) { PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw,  mbd.allowCaching);if (hasInstAwareBpps) {
         for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
               InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
               // This is a BeanPostProcessor for @autowired that was mentioned above
               // It sets all attributes that mark @autoWired and @Value annotations
               pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
               if (pvs == null) {
                  return; }}}}if(needsDepCheck) { checkDependencies(beanName, mbd, filteredPds, pvs); }}// Set the property value of the bean instance
   applyPropertyValues(beanName, mbd, bw, pvs);
}

Copy the code
  1. finishRefresh()
protected void finishRefresh(a) {
  // Clear the resource cache used by the previous sequence of operations
  clearResourceCaches();

  // Initialize LifecycleProcessor
  initLifecycleProcessor();

  The internal implementation of this method is to start all beans that implement the Lifecycle interface
  getLifecycleProcessor().onRefresh();

  // Publish the ContextRefreshedEvent event
  publishEvent(new ContextRefreshedEvent(this));

  / / check spring. LiveBeansView. MbeanDomain exists, there will create a MBeanServer
  LiveBeansView.registerApplicationContext(this);
}

Copy the code

3. Afterword.

Here is only for the container initialization to do the overall analysis of the bean creation and acquisition also involves a lot of knowledge points. We’ll talk about that later