Spring IOC Process Source Parsing

Keywords: Java xml Spring encoding Attribute

Say nothing more, let's start with a dumb version of IOC demo

Custom Bean Definition

class MyBeanDefinition{

    public String id;
    public String className;
    public String value;

    public MyBeanDefinition(String id, String className, String value) {
        this.id = id;
        this.className = className;
        this.value = value;
    }

}

Custom Bean Factory


class MyBeanFactory {

    Map<String, Object> beanMap = new HashMap<>();

    public MyBeanFactory(MyBeanDefinition beanDefinition) throws ClassNotFoundException,
            IllegalAccessException, InstantiationException {

        Class<?> beanClass = Class.forName(beanDefinition.className);
        Object bean = beanClass.newInstance();
        ((UserService) bean).setName(beanDefinition.value);
        beanMap.put(beanDefinition.id, bean);

    }

    public Object getBean(String id) {
        return beanMap.get(id);
    }


}

Test fool version IOC container

public class EasyIOC {

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {

        MyBeanDefinition beanDefinition = new MyBeanDefinition("userService",
                "com.valarchie.UserService", "archie");

        MyBeanFactory beanFactory = new MyBeanFactory(beanDefinition);
        UserService userService = (UserService) beanFactory.getBean("userService");

        System.out.println(userService.getName());

    }


}

After watching this silly version of the example above, can we think about it?What is the key to enabling our own container for IOC?

As I understand it, I summarize it in three steps

  • Read xml file to form DOM object
  • Read Bean Definitions from DOM Document Objects and load them into BeanFactory
  • Generate instances from bean definitions into containers for use

So next we won't analyze the entire IOC process in a holistic way, because there are too many side details for the reader to focus on.
We understand the IOC process by analyzing the most important code backbone.

Start analysis:

First, we start with the configuration of xml, because Spring was initially configured using xml, so most people are familiar with and easy to understand the configuration of xml.

Start with the constructor for ClassPathXmlApplicationContext.

public class TestSpring {
    public static void main(String[] args) {
        // The IOC container starts with the construction method of ClassPathXmlApplicationContext
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
        UserService userService = (UserService) context.getBean("userService");
        System.out.println(userService.getName());
       
    }
}

Going into a construction method calls another overloaded construction method.

// Create a ClassPathXmlApplicationContext, load the xml file at the given location, and automatically refresh the context
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
}

In an overloaded construction method, the parent container is not set because the parrent parameter was just passed to null.Refresh was just set to true, and the process enters the refresh() method

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {
        // Since the previous method call set parent to null, we will not analyze it
        super(parent);
        // Setting up an array of paths and replacing the configuration paths with simple placeholders in turn is simpler, and we won't go into the analysis either.
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
}

The entire refresh() method is the backbone of the IOC container startup. Spring uses the template method design pattern to design the refresh() method, specifying the specific steps for the entire IOC container, and then implementing each small step by its own subclasses.

All the important operations are around BeanFactory.
In the notes, we detail what each step of the process accomplishes.Instances of FactoryBean are held inside the ApplicationContext.Actually, the top-level parent interface of the ApplicationContext itself is also BeanFactory, which extends beyond BeanFactory to provide international message access, resource access, such as URL s and files, event propagation, loading multiple (inherited) contexts.

Let's start by reading the comments in the code to get a general idea.

public void refresh() throws BeansException, IllegalStateException {
        // Lock first to prevent start and end conflicts
        synchronized (this.startupShutdownMonitor) {
            // Do some preparatory work before refreshing
            // Set the start time, flag bits (active, closed), initialize placeholder property source, and confirm
            // Each attribute marked as required is resolvable.
            prepareRefresh();

            // Gets a refreshed BeanFactory instance.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Define the environmental characteristics of the Bean Factory, such as a class loader or a post-processor
            prepareBeanFactory(beanFactory);

            try {
                // Set up some post-operations after BeanFactory has finished initializing, leaving spring for the extension of the subclass.
                postProcessBeanFactory(beanFactory);

                // BeanFactory Post Processor Set Up Before Startup
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register Bean Processor
                registerBeanPostProcessors(beanFactory);

                // Set the message source for our application context (i18n)
                initMessageSource();

                // Initialize event broadcaster
                initApplicationEventMulticaster();

                // Initialize special beans In special ontexts, the default implementation is empty and handed over to specific subclasses for implementation
                onRefresh();

                // Check listeners and register
                registerListeners();

                // Instantiate all non-lazy loaded beans
                finishBeanFactoryInitialization(beanFactory);

                // The last step is to publish the appropriate event
                finishRefresh();
            }

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

                // If the startup fails, destroy the Beans you created earlier.
                destroyBeans();

                // Reset the flag bit of the internal active in the ApplicationContext
                cancelRefresh(ex);

                // Throw an exception to the caller
                throw ex;
            }

            finally {
                // Reset the cache within the Spring core because we may no longer need the metadata associated with a single bean
                resetCommonCaches();
            }
        }
    }

After reading this, we focus on the obtainFreshBeanFactory() and finishBeanFactoryInitialization(beanFactory) methods, since essentially the entire IOC process is one of these two methods, the other being Spring's custom actions reserved for the user, such as BeanFactory's postprocessor and Bean's postpositionProcessors are part of the publishing and monitoring of critical startup events and part of the operation on AOP.

First, start with obtainFreshBeanFactory().

Step 1: Read the xml file to form a DOM object

Before the getBeanFactory() method, call the refreshBeanFactory() method to refresh.Let's start by saying that getBeanFactory () is very simple, and the default implementation simply returns the bean factory that was successfully built with the last refresh step.The returned Bean factory has loaded the Bean definition.So the refreshBeanFactory () method already contains the first step to read the xml file to construct a DOM object and the second step to parse the element in the DOM to generate a Bean definition for saving.Keep in mind that this is just about saving the Bean definition, and there is no instantiation of the Bean at this time.

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        }
        return beanFactory;
}

Enter the refreshBeanFactory() method

protected final void refreshBeanFactory() throws BeansException {
        // Destroy if FactoryBean already exists in the current ApplicationContext
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            // Mr. Generate a BeanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            // Set up serialization
            beanFactory.setSerializationId(getId());
            // Whether the settings can override Bean definitions and whether they can be circularly dependent is beyond my explanation
            customizeBeanFactory(beanFactory);
            // Load Bean Definition into Factory
            // A key!
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

Next, you enter the core method, loadBeanDefinitions(beanFactory), whose parameter is the beanFactory you just created

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create an xml reader from the incoming beanfactory
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

    
        // Setting the resource loading environment associated with the bean definition reader
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // This method allows subclasses to customize the initialization of Reader
        initBeanDefinitionReader(beanDefinitionReader);
        // Next, you begin to actually load the Bean definition
        loadBeanDefinitions(beanDefinitionReader);
    }

In the loadBeanDefinitions(beanDefinitionReader) method, the parameter is the Reader reader that has just been created.

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        // If there is a Resouce instance already generated, it will be resolved directly.
        // The default implementation is to return null, which is implemented by the subclass itself.
        Resource[] configResources = getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }
        // Path resolution is performed without Resouces.
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            reader.loadBeanDefinitions(configLocations);
        }
}

We go into the reader.loadBeanDefinitions(configLocations) method, where the method call is a bit around, and I'll just briefly describe it here

This method will process xml files in different locations in turn.
Different writings of paths, such as classpath or WEB-INF prefix paths, are then processed differently.
Generate corresponding Resouces from the incoming locations variable.
Next, you enter reader.loadBeanDefinitions(resource), where the parameter is Resource.
Enter the method call of the loadBeanDefinitions (new EncodedResource (resource) one level later.

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    Assert.notNull(encodedResource, "EncodedResource must not be null");
    if (logger.isInfoEnabled()) {
        logger.info("Loading XML bean definitions from " + encodedResource.getResource());
    }
    // Current CurrtResource implemented through ThreadLocal
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet<EncodedResource>(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    if (!currentResources.add(encodedResource)) {
        throw new BeanDefinitionStoreException(
                "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }
    try {
    
        // The main method is in this section
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            // Incoming stream object with encoding set
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        finally {
            inputStream.close();
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException(
                "IOException parsing XML document from " + encodedResource.getResource(), ex);
    }
    finally {
        currentResources.remove(encodedResource);
        if (currentResources.isEmpty()) {
            this.resourcesCurrentlyBeingLoaded.remove();
        }
    }
}

The main purpose of this method is to create the corresponding input stream and set up the encoding.

Then start calling the doLoadBeanDefinitions() method.

// Internal core code for these two sentences
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
        

In the loadDocument() method, a DocumentBuilderImpl object is generated, which calls the parse method, parses the InputSource wrapped by the input stream with SAX in the parse method, and generates the DOM object return.

public Document parse(InputSource is) throws SAXException, IOException {
        if (is == null) {
            throw new IllegalArgumentException(
                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
                "jaxp-null-input-source", null));
        }
        if (fSchemaValidator != null) {
            if (fSchemaValidationManager != null) {
                fSchemaValidationManager.reset();
                fUnparsedEntityHandler.reset();
            }
            resetSchemaValidator();
        }
        // parse xml
        domParser.parse(is);
        // Get dom just parsed
        Document doc = domParser.getDocument();
        domParser.dropDocumentReferences();
        return doc;
}

Now that our xml file has been loaded and parsed into a DOM structure object, the first step is complete.

Step 2: Read the Bean definition in the DOM document object and load it into the BeanFactory

// Internal core code for these two sentences
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
        

Let's go back to the two core codes we just talked about. The first sentence gets the DOM object, then the second sentence registerBeanDefinitions(doc, resource) starts the registration of the bean definition.

Enter the registerBeanDefinitions(doc, resource) method

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // Generate a DOM reader, which is different from the previous one, which was an xml reader.
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        // Get the number of bean s defined before 
        int countBefore = getRegistry().getBeanDefinitionCount();
        
        // Enter focus
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        
        // Define number with beans just created - number of beans previously defined = beans newly created together 
        return getRegistry().getBeanDefinitionCount() - countBefore;
}

Enter the documentReader.registerBeanDefinitions (doc, createReaderContext (resource) method.
The root element within the method that reads the document.

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;
        
        // Generate Bean Definition Resolution Class
        this.delegate = createDelegate(getReaderContext(), root, parent);

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

        // Extensibility methods reserved by spring for subclasses
        preProcessXml(root);
        
        // A key
        // Start parsing Bean definitions
        parseBeanDefinitions(root, this.delegate);
        
      // Extensibility methods reserved by spring for subclasses
        postProcessXml(root);

        this.delegate = parent;
        
}

Enter parseBeanDefinitions(root, this.delegate).Pass in the previous document object and bean definition parsing class as parameters.

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            // Traverse to resolve each child node element of the root node
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                // If it is a label element
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                    
                        // Resolve default elements
                        // A key
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        // Resolve specified custom elements
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            // For custom parsing, which is not the default namespace, the namespace is the xmlns in the xml document header that defines the label.
            delegate.parseCustomElement(root);
        }
}

When you step into the parseDefaultElement(ele, delegate), you will find that it resolves each of the four tags separately.

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)) {
            // Analyzing Bean Tags
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
}

We mainly analyze the parsing of Bean element tags and enter the innermost layer of the processBean Definition (ele, delegate) method.

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        // Get the id inside the bean tag
        String id = ele.getAttribute(ID_ATTRIBUTE);
        // Get the name inside the bean tag
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        // Set Multiple Aliases
        List<String> aliases = new ArrayList<String>();
        if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }

        // When no id is set
        String beanName = id;
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
            if (logger.isDebugEnabled()) {
                logger.debug("No XML 'id' specified - using '" + beanName +
                        "' as bean name and " + aliases + " as aliases");
            }
        }

        // Check if beanName is unique
        if (containingBean == null) {
            checkNameUniqueness(beanName, aliases, ele);
        }
        // Bean tag parsing done internally
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
                try {
                    if (containingBean != null) {
                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                beanDefinition, this.readerContext.getRegistry(), true);
                    }
                    else {
                        beanName = this.readerContext.generateBeanName(beanDefinition);
                        // Register an alias for the plain bean class name, if still possible,
                        // if the generator returned the class name plus a suffix.
                        // This is expected for Spring 1.2/2.0 backwards compatibility.
                        String beanClassName = beanDefinition.getBeanClassName();
                        if (beanClassName != null &&
                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                            aliases.add(beanClassName);
                        }
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Neither XML 'id' nor 'name' specified - " +
                                "using generated bean name [" + beanName + "]");
                    }
                }
                catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
}

Fill in the new BeanDefinition (beanDefinition, beanName, aliasesArray) with the parsed Bean definition and an alias array to return.Then call the following method.

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

The main thing to do is put the beanDefinitionMap into the beanDefinitionMap.

Save the Bean definition after parsing successfully.The second step has also been completed.

Step 3: Start instantiating the Bean using the created Bean definition.

Back in the original refresh method, we started instantiating the non-lazily loaded Bean object in the finishBeanFactoryInitialization(beanFactory) method.We follow the call chain into the preInstantiateSingletons() method

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

    // Put a copy of the beanDefinition Name list you made earlier into beanNames and start traversing
    List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

    // Trigger the instantiation of all non-lazy loaded singleton beans
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        
        // If not abstract and singleton and not lazy to load
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            // Detects if it is a factory method Bean.Different ways to create beans, readers can Baidu themselves.
            if (isFactoryBean(beanName)) {
                final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                boolean isEagerInit;
                if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                    isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                        @Override
                        public Boolean run() {
                            return ((SmartFactoryBean<?>) factory).isEagerInit();
                        }
                    }, getAccessControlContext());
                }
                else {
                    isEagerInit = (factory instanceof SmartFactoryBean &&
                            ((SmartFactoryBean<?>) factory).isEagerInit());
                }
                if (isEagerInit) {
                    getBean(beanName);
                }
            }
            else {
                getBean(beanName);
            }
        }
    }

    // Omitting custom action code after instantiation...
}

In this method, depending on whether the Bean instance is factory or generic, the most important method is the getBean(beanName) method.We continue to analyze the process of common instantiation.Entering the doGetBean() method of getBean() method, we find that the last three parameters of the method parameter doGetBean(name, null, null, false) are all null, which is the core code in the whole IOC.

The code first instantiates the Bean, and after instantiating it, it determines what dependencies the Bean needs, and makes recursive calls to instantiate the Bean, which completes the core process of the entire IOC.

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

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

    // Eagerly check singleton cache for manually registered singletons.
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
        if (logger.isDebugEnabled()) {
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
                        "' that is not fully initialized yet - a consequence of a circular reference");
            }
            else {
                logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }

    else {
        // If the current Bean is being created, it will fail directly.
        // You can get stuck in a circular reference.
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }

        // Detect whether the Bean definition appears in the parent Bean factory
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
            // When the parent factory is not null and does not currently contain this Bean definition
            // Return to Bean from parent factory
            String nameToLookup = originalBeanName(name);
            if (args != null) {
                // Delegation to parent with explicit args.
                return (T) parentBeanFactory.getBean(nameToLookup, args);
            }
            else {
                // No args -> delegate to standard getBean method.
                return parentBeanFactory.getBean(nameToLookup, requiredType);
            }
        }

            // Mark as created if type checking is not required
        if (!typeCheckOnly) {
            markBeanAsCreated(beanName);
        }

        try {
            // Integrate child and parent Bean definitions
            final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // An exception cannot be thrown by an instance if an abstract class is found after integration
            checkMergedBeanDefinition(mbd, beanName, args);

            // Get the required dependencies for the Bean definition and initialize the fill one by one
            String[] dependsOn = mbd.getDependsOn();
            if (dependsOn != null) {
                for (String dep : dependsOn) {
                    // Determine whether circular dependency exists
                    if (isDependent(beanName, dep)) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
                    }
                    // Register Dependent Bean s
                    registerDependentBean(dep, beanName);
                    try {
                    // Recursive calls to generate beans on which to rely
                        getBean(dep);
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
                    }
                }
            }

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

            // If prototype
            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);
            }
                    // Non-singleton and prototype range cases
            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() throws BeansException {
                            beforePrototypeCreation(beanName);
                            try {
                                return createBean(beanName, mbd, args);
                            }
                            finally {
                                afterPrototypeCreation(beanName);
                            }
                        }
                    });
                    bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
                }
                catch (IllegalStateException ex) {
                    throw new BeanCreationException(beanName,
                            "Scope '" + scopeName + "' is not active for the current thread; consider " +
                            "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                            ex);
                }
            }
        }
        catch (BeansException ex) {
            cleanupAfterBeanCreationFailure(beanName);
            throw ex;
        }
    }

    // Detect if the type of instance Bean is consistent with the required type
    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 new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
    }
    return (T) bean;
}

Destantiate a Bean based on its definition.The third step has also been completed.

The article is limited in length, and the whole process of IOC creation is rather lengthy. I want readers to read the article after they have a main idea of IOC creation process or need to open the source code for interpretation. In fact, it is not difficult to read the source code, because Spring's code comments are sound, if you encounter a slightly unclear google.You know it in a minute.It is recommended that readers try to analyze the source code of the IOC process step by step.

Go to my personal blog vc2x.com

Posted by Rob2005 on Mon, 09 Sep 2019 19:15:10 -0700