preface

Common concepts are not introduced (for example, some of the injection methods are available in the official documentation, which will be linked at the end of the document). Here we grasp the backbone, the god perspective of this IOC principle simple touch

The basic concept

From an official high-level view, it’s easy to understand: by configuring the container (in annotation/XML form) to create the object for us, we just need to get and do it; The container decouples the creation and destruction of object instances from the call process. The caller only needs to care about the object itself, and the container manages the survival of the object for us

Container high-level schematic diagram
image-20191231104332673

When Spring starts, it reads the Bean configuration information provided by the application program, and generates a corresponding Bean configuration registry in the Spring container. Then, it instantiates beans according to this registry, assembs the dependencies between beans, and provides a ready running environment for the upper application. The Bean cache pool is a HashMap implementation

The core of

BeanDefinition

Is an interface within Spring that defines the basic specification for beans (beanClassName, Scope, lazyInit, etc.) and the abstraction of objects in containers;

Property Explained in…
Class Instantiating Beans
Name Naming Beans
Scope Bean Scopes
Constructor arguments Dependency Injection
Properties Dependency Injection
Autowiring mode Autowiring Collaborators
Lazy initialization mode Lazy-initialized Beans
Initialization method Initialization Callbacks
Destruction method Destruction Callbacks

The official documentation gives the following instructions:

In addition to bean definitions that contain information on how to create a specific bean, the ApplicationContext implementations also permit the registration of existing objects that are created outside the container (by users). This is done by accessing the ApplicationContext’s BeanFactory through the getBeanFactory() method, which returns the BeanFactory DefaultListableBeanFactory implementation. DefaultListableBeanFactory supports this registration through the registerSingleton(..) and registerBeanDefinition(..) methods. However, typical applications work solely with beans defined through regular bean definition metadata.

Machine turn:

In addition to bean definitions that contain information about how a particular bean is created, the ApplicationContext implementation also allows you to register existing objects that are created outside the container (by the user). This is done by the method access ApplicationContext the BeanFactory getBeanFactory (), the method returns the BeanFactory DefaultListableBeanFactory implementation. DefaultListableBeanFactory through registerSingleton (..) And registerBeanDefinition (..) Method supports this registration. However, a typical application can only work with beans defined through regular bean definition metadata.

In a nutshell

So how do you play with this BeanDefinition thing, you register it with getBeanFactory().registerBeanDefinition()

BeanFactory

At the top of the class structure tree, its main method is getBean(String beanName), which returns a Bean with a specific name from the container. The functionality of BeanFactory is extended through other interfaces

image-20191231103355286

ApplicationContext

ApplicationContext is derived from BeanFactory and provides more application-oriented functionality. The ApplicationContext inherits the HierarchicalBeanFactory and ListableBeanFactory interfaces, and on top of that extends the functionality of the BeanFactory through several other interfaces

image-20191231104029993
  1. FileSystemXmlApplicationContext: from the file system to load the configuration file by default

  2. ApplicationEventPublisher: let the container has released application context function of events, including container startup

    File, close event, etc.

  3. MessageSource: Provides i18N international message access for applications;

  4. ResourcePatternResolver: All ApplicationContext implementation classes implement things like

    PathMatchingResourcePatternResolver function, can be prefixed Ant style of resource

    The installation path loads the Spring configuration file.

  5. LifeCycle: Added to Spring 2.0, this interface provides the start() and stop() methods, primarily

    Used to control asynchronous processing. This interface is implemented by both the ApplicationContext and the Bean. The ApplicationContext passes the start/stop information to all the beans in the container that implement the interface. To achieve management and control JMX, task scheduling and other purposes.

  6. ConfigurableApplicationContext extension in ApplicationContext, it added two main methods: Refresh () and close(), giving ApplicationContext the ability to start, refresh, and close the ApplicationContext. Call refresh() to start the application context when it is closed, call refresh() to clear the cache and reload configuration information when it is already started, and call close() to close the application context.

The principle of analysis

With the introduction of the previous basic concepts,IOC’s three axe

  • The bean configuration information is encapsulated in the BeanDefinition and inserted into the registry. Through the getBeanFactory (.) registerBeanDefinition ()
  • The beans are parsed and instantiated into the Map cache based on the registry information
  • The caller getBean gets the corresponding bean

So make clear the above three steps, the general principle of our door is clear

The demo sample is simple

Java code

@Setter

@Getter

public class AliasDemo {

    private String content;

}

/ / test methods

@Test

public void testAlias(a) {

  String configLocation = "application-alias.xml";

  ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);

  System.out.println(" alias-hello -> " + applicationContext.getBean("alias-hello"));

}

Copy the code

The XML configuration


       

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans"

       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"


       default-lazy-init="true">


    <context:component-scan base-package="com.zhangpeng.**"/>

    <bean id="hello" class="com.zhangpeng.study.ioc.alias.AliasDemo">

        <property name="content" value="hello"/>

    </bean>

    <alias name="hello" alias="alias-hello"/>

</beans>

Copy the code

debug

1. Read the IOC configuration

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {

@Nullable

    private Resource[] configResources;

. Omitting part of the source...

    // This is the construct we jumped into in the previous example, passing in a configuration path

    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {

        this(new String[] {configLocation}, true.null);

    }

    // The overloaded constructor has setConfigLocations,

    // Set the configuration path

    // There is also a refresh method

    public ClassPathXmlApplicationContext(

            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)


            throws BeansException 
{



        super(parent);

        setConfigLocations(configLocations);

        if (refresh) {

            refresh();

        }

    }

}

Copy the code

SetConfigLocations is basically configuring some path

2. Core Refresh method

@Override

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.

    // 1. Get a new BeanFactory instance

    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

2.1 obtainFreshBeanFactory ()

@Override

protected final void refreshBeanFactory(a) throws BeansException {

  if (hasBeanFactory()) {

    destroyBeans();

    closeBeanFactory();

  }

  try {

    DefaultListableBeanFactory beanFactory = createBeanFactory();

    beanFactory.setSerializationId(getId());

    customizeBeanFactory(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

It’s a good name, it’s easy to understand

  • Come in to destroy the beanFactory and create a DefaultListableBeanFactory instance
  • CustomizeBeanFactory Customizes some attributes (whether BeanDefinition can be overridden, whether circular dependencies are supported, etc.)

Then enter loadBeanDefinitions

//org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factor y.support.DefaultListableBeanFactory)

@Override

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

  loadBeanDefinitions(beanDefinitionReader);

}

Copy the code

Then go to the doLoadBeanDefinitions, which is aptly named xx -> doXx

// Let's look at the details

/ * *

     * Actually load bean definitions from the specified XML file.

     * @param inputSource the SAX InputSource to read from

     * @param resource the resource descriptor for the XML file

     * @return the number of bean definitions found

     * @throws BeanDefinitionStoreException in case of loading or parsing errors

     * @see #doLoadDocument

     * @see #registerBeanDefinitions

* /


protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)

  throws BeanDefinitionStoreException 
{

 / /... Omit...

  try {

    Document doc = doLoadDocument(inputSource, resource);

    int count = registerBeanDefinitions(doc, resource);

    if (logger.isDebugEnabled()) {

      logger.debug("Loaded " + count + " bean definitions from " + resource);

    }

    return count;

  }

    / /... Omit...

}

Copy the code

Then see registerBeanDefinitions

//org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#registerBeanDefinitions

    / * *

     * This implementation parses bean definitions according to the "spring-beans" XSD

     * (or DTD, historically).

     * <p>Opens a DOM Document; then initializes the default settings

     * specified at the {@code <beans/>} level; then parses the contained bean definitions.

* /


    @Override

    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {

        this.readerContext = readerContext;

        doRegisterBeanDefinitions(doc.getDocumentElement());

    }

//org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions

        / * *

     * Register each bean definition within the given root {@code <beans/>} element.

* /


    @SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)

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

      / /... Omit...

    // Standard start pre

        preProcessXml(root);

    / / doc

        parseBeanDefinitions(root, this.delegate);

    // standard end post after

        postProcessXml(root);

        this.delegate = parent;

    }

Copy the code

Oh, my god, another dose of doXX. That’s great

Spring, you old driver

And finally, parseBeanDefinitions without going into all the details we’re going to focus on the main principle

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)) {

          parseDefaultElement(ele, delegate);

        }

        else {

          delegate.parseCustomElement(ele);

        }

      }

    }

  }

  else {

    delegate.parseCustomElement(root);

  }

}

 // Parse the default element

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {

        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {

            importBeanDefinitionResource(ele);

        }

        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {

            processAliasRegistration(ele);

        }

        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {

            processBeanDefinition(ele, delegate);

        }

        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {

            // recurse

            doRegisterBeanDefinitions(ele);

        }

    }

// Where the BeanDefinition is actually resolved

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

  // This step takes BeanDefinitionHolder to further encapsulate BeanDefinition data

  BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

  if(bdHolder ! =null) {

    bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

    try {

      // Register the final decorated instance.

      // Register the final decorator instance

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

    }

    catch (BeanDefinitionStoreException ex) {

      getReaderContext().error("Failed to register bean definition with name '" +

                               bdHolder.getBeanName() + "'", ele, ex);

    }

    // Send registration event.

    getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

  }

}

Copy the code

At this point,BeanDefinition is born and encapsulated in BeanDefinitionHolder, which answers the first half of the IOC tripartite question. BeanDefinition is resolved by configuration

Now that I have a BeanDefinition, I should register it, and continue with the code above

    / * *

     * Register the given bean definition with the given bean factory.

     * @param definitionHolder the bean definition including name and aliases

     * @param registry the bean factory to register with

     * @throws BeanDefinitionStoreException if registration failed

* /


/ / by specifying the bean factory registration, the default is DefaultListableBeanFactory

    public static void registerBeanDefinition(

            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)


            throws BeanDefinitionStoreException 
{



        // Register bean definition under primary name.

        String beanName = definitionHolder.getBeanName();

        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());



        // Register aliases for bean name, if any.

        String[] aliases = definitionHolder.getAliases();

        if(aliases ! =null) {

            for (String alias : aliases) {

                registry.registerAlias(beanName, alias);

            }

        }

    }

// org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

@Override

    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 existingDefinition = this.beanDefinitionMap.get(beanName);

        if(existingDefinition ! =null) {

            if(! isAllowBeanDefinitionOverriding()) {

                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);

            }

            else if (existingDefinition.getRole() < beanDefinition.getRole()) {

                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE

                if (logger.isInfoEnabled()) {

                    logger.info("Overriding user-defined bean definition for bean '" + beanName +

                            "' with a framework-generated bean definition: replacing [" +

                            existingDefinition + "] with [" + beanDefinition + "]");

                }

            }

            else if(! beanDefinition.equals(existingDefinition)) {

                if (logger.isDebugEnabled()) {

                    logger.debug("Overriding bean definition for bean '" + beanName +

                            "' with a different definition: replacing [" + existingDefinition +

                            "] with [" + beanDefinition + "]");

                }

            }

            else {

                if (logger.isTraceEnabled()) {

                    logger.trace("Overriding bean definition for bean '" + beanName +

                            "' with an equivalent definition: replacing [" + existingDefinition +

                            "] 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<>(this.beanDefinitionNames.size() + 1);

                    updatedDefinitions.addAll(this.beanDefinitionNames);

                    updatedDefinitions.add(beanName);

                    this.beanDefinitionNames = updatedDefinitions;

                    removeManualSingletonName(beanName);

                }

            }

            else {

                // Still in startup registration phase

                this.beanDefinitionMap.put(beanName, beanDefinition);

                this.beanDefinitionNames.add(beanName);

                removeManualSingletonName(beanName);

            }

            this.frozenBeanDefinitionNames = null;

        }



        if(existingDefinition ! =null || containsSingleton(beanName)) {

            resetBeanDefinition(beanName);

        }

    }

Copy the code

The above is the process of DefaultListableBeanFactory will BeanDefinition into the map

Strategic summary

Analytical BeanDefinition obtainFreshBeanFactory method in the process of the acquisition, loading, to BeanDefinition encapsulation BeanDefinitionHolder, finally by DefaultListableBeanFactory note again Copies of BeanDefinition to map

1577785049585

Some brother

image-20191231173442143

3.getBean

Xx & doXx again

public <T> getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)

throws BeansException 
{

    return doGetBean(name, requiredType, args, false);

}



protected <T> doGetBean(final String name, @Nullable final Class<T> requiredType,

            @Nullable final Object[] args, boolean typeCheckOnly)
 throws BeansException 
{

        // 1. Remove the & character to get the FactoryBean itself 2. If it is an alias, convert it to a concrete instance name

        final String beanName = transformedBeanName(name);

        Object bean;



        <=>map.get(beanName) ,bean>

        Object sharedInstance = getSingleton(beanName);

        if(sharedInstance ! =null && args == null) {

            if (logger.isTraceEnabled()) {

                if (isSingletonCurrentlyInCreation(beanName)) {

                    logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +

                            "' that is not fully initialized yet - a consequence of a circular reference");

                }

                else {

                    logger.trace("Returning cached instance of singleton bean '" + beanName + "'");

                }

            }

      // Return if it is not null

            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

        }



        else {

      // BeanFactory does not cache Prototype beans and cannot handle loop dependencies for that type of bean

            if (isPrototypeCurrentlyInCreation(beanName)) {

                throw new BeanCurrentlyInCreationException(beanName);

            }



            // Check if bean definition exists in this factory.

            BeanFactory parentBeanFactory = getParentBeanFactory();

      // Find the bean instance from the parent container

            if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {

                // Not found -> check parent.

                String nameToLookup = originalBeanName(name);

                if (parentBeanFactory instanceof AbstractBeanFactory) {

                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(

                            nameToLookup, requiredType, args, typeCheckOnly);

                }

        // Enter the corresponding getBean overload method with or without arguments

                else if(args ! =null) {

                    // Delegation to parent with explicit args.

                    return (T) parentBeanFactory.getBean(nameToLookup, args);

                }

                else if(requiredType ! =null) {

                    // No args -> delegate to standard getBean method.

                    return parentBeanFactory.getBean(nameToLookup, requiredType);

                }

                else {

                    return (T) parentBeanFactory.getBean(nameToLookup);

                }

            }



            if(! typeCheckOnly) {

                markBeanAsCreated(beanName);

            }



            try {

        // Merge parent BeanDefinition

                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

                checkMergedBeanDefinition(mbd, beanName, args);



                // Check if there are any dependent beans

                String[] dependsOn = mbd.getDependsOn();

                if(dependsOn ! =null) {

                    for (String dep : dependsOn) {

            // Cyclic dependency check

                        if (isDependent(beanName, dep)) {

                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,

                                    "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");

                        }

            // Register the dependent bean information

                        registerDependentBean(dep, beanName);

                        try {

              // Load the dependent bean

                            getBean(dep);

                        }

                        catch (NoSuchBeanDefinitionException ex) {

                            throw new BeanCreationException(mbd.getResourceDescription(), beanName,

                                    "'" + beanName + "' depends on missing bean '" + dep + "'", ex);

                        }

                    }

                }



                // Create the bean instance

                if (mbd.isSingleton()) {

                    sharedInstance = getSingleton(beanName, () -> {

                        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;

                        }

                    });

          // If the bean is of type FactoryBean, the factory method is called to get the real bean instance, otherwise the bean instance is returned directly

                    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

                }

                // Create a prototypebean

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

                }

                // Create another type of bean

                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, () -> {

                            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 a type conversion is required, and return if so

        if(requiredType ! =null && !requiredType.isInstance(bean)) {

            try {

                T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);

                if (convertedBean == null) {

                    throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());

                }

                return convertedBean;

            }

            catch (TypeMismatchException ex) {

                if (logger.isTraceEnabled()) {

                    logger.trace("Failed to convert bean '" + name + "' to required type '" +

                            ClassUtils.getQualifiedName(requiredType) + "'", ex);

                }

                throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());

            }

        }

        return (T) bean;

    }

Copy the code

Let’s converge the main logic above

image-20200102165520545
  • 1. BeanName transformation

  • 2. Get the bean from the cache

  • 3. GetObjectForBeanInstance is called immediately if the instance is not constructed with empty & empty parameters

  • 4. If condition 3 is not met, find the bean by looking for the BeanFactory of the parent container

    There is no way to find the corresponding bean directly by returning the parent container

  • 5. The bean exists and the parent BeanDefinition is merged

  • 6. Handle depends on beans, register & create

  • 7. Working with FactoryBeans (same process as 4)

  • 8. If a cast is required, the cast returns the bean

3.1 beanName conversion

    protected String transformedBeanName(String name) {

        return canonicalName(BeanFactoryUtils.transformedBeanName(name));

    }

  // It's easy to get the factoryBean itself rather than the object it implements

    public static String transformedBeanName(String name) {

        Assert.notNull(name, "'name' must not be null");

        if(! name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {

            return name;

        }

        return transformedBeanNameCache.computeIfAbsent(name, beanName -> {

            do {

                beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());

            }

            while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));

            return beanName;

        });

    }

    The map does not support the storage mode ,bean>

    public String canonicalName(String name) {

        String canonicalName = name;

        // Handle aliasing...

        String resolvedName;

        do {

            resolvedName = this.aliasMap.get(canonicalName);

            if(resolvedName ! =null) {

                canonicalName = resolvedName;

            }

        }

        while(resolvedName ! =null);

        return canonicalName;

    }

Copy the code

The use of & is mentioned in the official documentation. Interested students can go to have a look

When you need to ask a container forAn actual FactoryBean instance itself instead of the bean it produces, preface the bean 's id with the ampersand symbol (&) when calling the getBean(a) method of the ApplicationContext. So, for a given FactoryBean with an id of myBean, invoking getBean("myBean") on the container returns the product of the FactoryBean, whereas invoking getBean("&myBean") returns the FactoryBean instance itself.

Copy the code

3.2 Cache Obtaining instances

    @Nullable

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {

        Object singletonObject = this.singletonObjects.get(beanName);

        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {

            synchronized (this.singletonObjects) {

                // The main purpose is to prevent duplicate dependencies

                singletonObject = this.earlySingletonObjects.get(beanName);

                if (singletonObject == null && allowEarlyReference) {

                    // The addSingletionFactory method is called when some method needs to be pre-initialized and stores the corresponding singletonFactory in singletonFactories

ObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);

                    if(singletonFactory ! =null) {

                        EarlySingletonObjects and singletonFactories are mutually exclusive

                        singletonObject = singletonFactory.getObject();

                        this.earlySingletonObjects.put(beanName, singletonObject);

                        this.singletonFactories.remove(beanName);

                    }

                }

            }

        }

        return singletonObject;

    }

    /** Cache of singleton objects: bean name to bean instance. */

    // Directly usable objects

    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /** Cache of singleton factories: bean name to ObjectFactory. */

    // The bean factory is used to store the bean factory. The beans generated by the bean factory are not initialized yet. The objects generated by the bean factory are eventually cached in earlySingletonObjects

    private finalMap<String, ObjectFactory<? >> singletonFactories =new HashMap<>(16);

    // Early object references (beans still being initialized)

    /** Cache of early singleton objects: bean name to bean instance. */

    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

Copy the code

This code deals with the handling of loop dependencies, which will be covered in a separate article

3.3 getObjectForBeanInstance return

protected Object getObjectForBeanInstance(

            Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd)
 
{

        // If it starts with &, then you get the FactoryBean instance

        if (BeanFactoryUtils.isFactoryDereference(name)) {

            if (beanInstance instanceof NullBean) {

                return beanInstance;

            }

      // Not a FactoryBean type

            if(! (beanInstanceinstanceof FactoryBean)) {

                throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());

            }

            if(mbd ! =null) {

                mbd.isFactoryBean = true;

            }

            return beanInstance;

        }

        // Name does not start with & and beanInstance is not a FactoryBean, indicating that the current bean is a normal bean

        if(! (beanInstanceinstanceof FactoryBean)) {

            return beanInstance;

        }



        Object object = null;

        if(mbd ! =null) {

            mbd.isFactoryBean = true;

        }

        else {

      // Fetch from cache

            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)) {

        / / beanDefinition merger

                mbd = getMergedLocalBeanDefinition(beanName);

            }

            booleansynthetic = (mbd ! =null && mbd.isSynthetic());

      // Get the return from the FactoryBean

object = getObjectFromFactoryBean(factory, beanName, ! synthetic);

        }

        return object;

    }



/ / = = = = = = = = = = = = = = = = = = = = = = = = =



protected Object getObjectFromFactoryBean(FactoryBean<? > factory, String beanName,boolean shouldPostProcess) {

  if (factory.isSingleton() && containsSingleton(beanName)) {

    synchronized (getSingletonMutex()) {

      Object object = this.factoryBeanObjectCache.get(beanName);

      if (object == null) {

        // where factory.getobject () is actually called

        object = doGetObjectFromFactoryBean(factory, beanName);

        Object alreadyThere = this.factoryBeanObjectCache.get(beanName);

        if(alreadyThere ! =null) {

          object = alreadyThere;

        }

        else {

          // Whether to apply a rear processor

          if (shouldPostProcess) {

            if (isSingletonCurrentlyInCreation(beanName)) {

              // Temporarily return non-post-processed object, not storing it yet..

              return object;

            }

            beforeSingletonCreation(beanName);

            try {

              // Execute the post-processor

              object = postProcessObjectFromFactoryBean(object, beanName);

            }

            catch (Throwable ex) {

              throw new BeanCreationException(beanName,

                                              "Post-processing of FactoryBean's singleton object failed", ex);

            }

            finally {

              afterSingletonCreation(beanName);

            }

          }

          if (containsSingleton(beanName)) {

            // Instances created by a FactoryBean are cached in factoryBeanObjectCache for subsequent calls

            this.factoryBeanObjectCache.put(beanName, object);

          }

        }

      }

      return object;

    }

  }

  // Get instances of non-singleton types

  else {

    // Factory class fetch

    Object object = doGetObjectFromFactoryBean(factory, beanName);

    // As above, whether post-processing is required

    if (shouldPostProcess) {

      try {

        object = postProcessObjectFromFactoryBean(object, beanName);

      }

      catch (Throwable ex) {

        throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);

      }

    }

    return object;

  }

}

/ / = = = = = = = = = = = = = = = = = = = = = = = = =

private Object doGetObjectFromFactoryBean(finalFactoryBean<? > factory,final String beanName)

  throws BeanCreationException 
{



  Object object;

  try {

    // Security check

    if(System.getSecurityManager() ! =null) {

      / /... Omit...

    }

    else {

      // The factory method generates the instance

      object = factory.getObject();

    }

  }

  / /... Omit...

  if (object == null) {

    if (isSingletonCurrentlyInCreation(beanName)) {

      throw new BeanCurrentlyInCreationException(

        beanName, "FactoryBean which is currently in creation returned null from getObject");

    }

    object = new NullBean();

  }

  return object;

}

Copy the code

Let’s go through the steps above. The code is long and the logic is clear

  • Check whether the type of a FactoryBean starts with &
  • The validation parameter beanInstance is not a FactoryBean, indicating that the current bean is a normal bean and is returned directly
  • Get the FactoryBean from the cache
  • For singleton type FactoryBean, if not hit, the instance is generated and cached from factoryBean.getobject ()
  • For factoryBeans that are not singletons, new instances are created directly without caching
  • ShouldPostProcess is used to determine whether the corresponding post-processor should be executed

3.4 Find beans by looking for the parent container’s BeanFactory

// Get the parent BeanFactory

BeanFactory parentBeanFactory = getParentBeanFactory();

// Verify that the bean does not exist

if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {

                  // Not found -> check parent.

                // If beanName is not found in XML, go to parentBeanFactory

                String nameToLookup = originalBeanName(name);

                if (parentBeanFactory instanceof AbstractBeanFactory) {

                    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(

                            nameToLookup, requiredType, args, typeCheckOnly);

                }

                else if(args ! =null) {

                    // Delegation to parent with explicit args.

                    return (T) parentBeanFactory.getBean(nameToLookup, args);

                }

                else if(requiredType ! =null) {

                    // No args -> delegate to standard getBean method.

                    return parentBeanFactory.getBean(nameToLookup, requiredType);

                }

                else {

                    return (T) parentBeanFactory.getBean(nameToLookup);

                }

}

Copy the code

3.5 If this bean exists, merge parent and child BeanDefinitions

final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {

  // Quick check on the concurrent map first, with minimal locking.

  RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);

  if(mbd ! =null && !mbd.stale) {

  return mbd;

  }

  return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));

}

protected RootBeanDefinition getMergedBeanDefinition(

            String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)


            throws BeanDefinitionStoreException 
{



        synchronized (this.mergedBeanDefinitions) {

            RootBeanDefinition mbd = null;

            RootBeanDefinition previous = null;



            // Check with full lock now in order to enforce the same merged instance.

            if (containingBd == null) {

                mbd = this.mergedBeanDefinitions.get(beanName);

            }



            if (mbd == null || mbd.stale) {

                previous = mbd;

                mbd = null;

        // Update BeanDefinition RootBeanDefinition

                if (bd.getParentName() == null) {

                    // Use copy of given root bean definition.

                    if (bd instanceof RootBeanDefinition) {

                        mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();

                    }

                    else {

                        mbd = new RootBeanDefinition(bd);

                    }

                }

                else {

                    // Child bean definition: needs to be merged with parent.

                    BeanDefinition pbd;

                    try {

                        String parentBeanName = transformedBeanName(bd.getParentName());

            // check whether the parentBeanName is the same as the child beanName. The same parentBeanName must be in map

                        if(! beanName.equals(parentBeanName)) {

              // Merge parent with parent using parentBeanName, and so on

                            pbd = getMergedBeanDefinition(parentBeanName);

                        }

                        else {

              ConfigurableBeanFactory ConfigurableBeanFactory ConfigurableBeanFactory

                            BeanFactory parent = getParentBeanFactory();

                            if (parent instanceof ConfigurableBeanFactory) {

                                pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);

                            }

                            else {

                                throw new NoSuchBeanDefinitionException(parentBeanName,

                                        "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +

                                        "': cannot be resolved without an AbstractBeanFactory parent");

                            }

                        }

                    }

                    catch (NoSuchBeanDefinitionException ex) {

                        throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,

                                "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);

                    }

          // The deep-copy form of the BeanDefinition is created by merging the parent BeanDefinition configuration

                    mbd = new RootBeanDefinition(pbd);

          // Override the parent BeanDefinition attribute with the child BeanDefinition

                    mbd.overrideFrom(bd);

                }



        // The configuration defaults to singletons if not specified

                if(! StringUtils.hasLength(mbd.getScope())) {

                    mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);

                }



                if(containingBd ! =null && !containingBd.isSingleton() && mbd.isSingleton()) {

                    mbd.setScope(containingBd.getScope());

                }



                // Cache the merged bean definition for the time being

                // (it might still get re-merged later on in order to pick up metadata changes)

                if (containingBd == null && isCacheBeanMetadata()) {

          // Cache merged definition

                    this.mergedBeanDefinitions.put(beanName, mbd);

                }

            }

            if(previous ! =null) {

                copyRelevantMergedBeanDefinitionCaches(previous, mbd);

            }

            return mbd;

        }

    }

Copy the code
  • BeanDefinition upgrade RootBeanDefinition
  • Check whether the parent beanName is the same as the child beanName
  • ConfigurableBeanFactory ConfigurableBeanFactory ConfigurableBeanFactory
  • You’ve done a lot of stuff and you’ve got to cache it for quick use

3.6 Handling Depends -on beans, registration & creation

// Guarantee initialization of beans that the current bean depends on.

String[] dependsOn = mbd.getDependsOn();

if(dependsOn ! =null) {

  // Instantiate the dependency recursively

  for (String dep : dependsOn) {

    // Check whether cyclic dependencies exist, and throw exceptions if so

    if (isDependent(beanName, dep)) {

      throw new BeanCreationException(mbd.getResourceDescription(), beanName,

                                      "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");

    }

    // Register dependencies

    registerDependentBean(dep, beanName);

    try {

      getBean(dep);

    }

    catch (NoSuchBeanDefinitionException ex) {

      throw new BeanCreationException(mbd.getResourceDescription(), beanName,

                                      "'" + beanName + "' depends on missing bean '" + dep + "'", ex);

    }

  }

}



/ * *

 * Register a dependent bean for the given bean,

 * to be destroyed before the given bean is destroyed.

 * @param beanName the name of the bean

 * @param dependentBeanName the name of the dependent bean

* /


public void registerDependentBean(String beanName, String dependentBeanName) {

  The map does not support the storage mode ,bean>

  String canonicalName = canonicalName(beanName);



  synchronized (this.dependentBeanMap) {

    Set<String> dependentBeans =

      this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));

    if(! dependentBeans.add(dependentBeanName)) {

      return;

    }

  }

 // set structure for de-weighting

  synchronized (this.dependenciesForBeanMap) {

    Set<String> dependenciesForBean =

      this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));

    dependenciesForBean.add(canonicalName);

  }

}

Copy the code

3.7 Handling FactoryBeans (same process as in 4) is omitted

3.8 If a cast is required, the cast returns the bean

The code process is well understood and I’m not going to go into it

summary

The getBean section is a bit longer, and the process is basically to instantiate the bean and stuff it into the cache and then return it to use

Technical summary

  • From IOC principle source code analysis,xx&doXx method naming style is worth practicing in the work
  • The routine of logical extraction is worth referring to

Further reading

Spring the official reference

END

If you like, you can connect them with one key. If you have any questions, please comment on the original git in the comment area