In this chapter, we continue to analyze what our container is doing.

We talked about what our ClassPathResource does, but when we read the source code, we found that it was just some initialization. Does our assumption that we get the configuration file and then go get the content from the configuration file still hold? There’s no need to rush. We’ll see the results step by step. Now that we’ve looked at ClassPathResource, let’s look at new XmlBeanFactory(ClassPathResource); What he did.

Construct the bean factory class

/**
 * Create a new XmlBeanFactory with the given resource,
 * which must be parsable using DOM.
 * @param resource the XML resource to load bean definitions from
 * @throws BeansException in case of loading or parsing errors
 */
public XmlBeanFactory(Resource resource) throws BeansException {
	this(resource, null);
}

/**
 * Create a new XmlBeanFactory with the given input stream,
 * which must be parsable using DOM.
 * @param resource the XML resource to load bean definitions from
 * @param parentBeanFactory parent bean factory
 * @throws BeansException in case of loading or parsing errors
 */
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
	super(parentBeanFactory);
	this.reader.loadBeanDefinitions(resource);
}
Copy the code

We are using the first construct, but the first construct calls the second, which has only two steps. Let’s move on

Take a look at the source code for XmlBeanFactory initialization

  • Super (parentBeanFactory) : used to initialize the parent class
  • This. Reader. LoadBeanDefinitions (resource) : spring initialization resource loading truly

Let’s start by looking at what the “super(parentBeanFactory) : used to initialize the parent class” step does

/ * * * first calls DefaultListableBeanFactory (@NullableBeanFactory parentBeanFactory) parameterized constructor */
public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
	super(parentBeanFactory);
}

/ * * * call the superclass AbstractAutowireCapableBeanFactory * /
public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
	this(a); setParentBeanFactory(parentBeanFactory); }/ * * * call AbstractAutowireCapableBeanFactory () * / no arguments constructor
public AbstractAutowireCapableBeanFactory(a) {
	super(a); ignoreDependencyInterface(BeanNameAware.class); ignoreDependencyInterface(BeanFactoryAware.class); ignoreDependencyInterface(BeanClassLoaderAware.class); }/** * Initialize the XmlBeanFactory container */
public void setParentBeanFactory(@Nullable BeanFactory parentBeanFactory) {
	if (this.parentBeanFactory ! =null && this.parentBeanFactory ! = parentBeanFactory) {throw new IllegalStateException("Already associated with parent BeanFactory: " + this.parentBeanFactory);
	}
	this.parentBeanFactory = parentBeanFactory;
}
Copy the code

Is it a little bit clear what we’re doing at this point? In fact, after looking at the source code of Super (parentBeanFactory) found to do two things: get add need to ignore the specified interface implementation class auto assembly and initialization. So what exactly did we do in step two?

This. Reader. LoadBeanDefinitions (resource) analysis

Earlier we said that we should find the corresponding class according to the configuration file, is that true of the source code? Just look at the implementation of this code.

/** * loadBeanDefinitions(Resource) */
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(new EncodedResource(resource));
}

/** * This method is exactly what we assumed to implement * loading beans from XML configuration files */
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	Assert.notNull(encodedResource, "EncodedResource must not be null");
	if (logger.isTraceEnabled()) {
		logger.trace("Loading XML bean definitions from " + encodedResource);
	}

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

When we post the code, is the actual evidence of our earlier speculation obvious?

ClassPathResource and InputStream

We can see from the source code above that the class we defined is in XML, and the source code contains InputStream. But only one ClassPathResource was passed in. So how does it encapsulate into the ClassPathResource?

The connection here is actually quite simple. Let’s take a look at the class structure diagram of these classes

Is it clear at this point how our InputStream passes content into the ClassPathResource

Actually getting the bean

Getting the bean is the most critical part of our operation. How do we get the bean? Let’s post the main code here

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
		@Nullable 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.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 + "'");
			}
		}
		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
	}

	else {
		// Fail if we're already creating this bean instance:
		// We're assumably within a circular reference.
		if (isPrototypeCurrentlyInCreation(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}

		// Check if bean definition exists in this factory.
		BeanFactory parentBeanFactory = getParentBeanFactory();
		if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
			// Not found -> check parent.
			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); }}if(! typeCheckOnly) { markBeanAsCreated(beanName); }try {
			final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
			checkMergedBeanDefinition(mbd, beanName, args);

			// Guarantee initialization of beans that the current bean depends on.
			String[] dependsOn = mbd.getDependsOn();
			if(dependsOn ! =null) {
				for (String dep : dependsOn) {
					if (isDependent(beanName, dep)) {
						throw new BeanCreationException(mbd.getResourceDescription(), beanName,
								"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
					}
					registerDependentBean(dep, beanName);
					try {
						getBean(dep);
					}
					catch (NoSuchBeanDefinitionException ex) {
						throw new BeanCreationException(mbd.getResourceDescription(), beanName,
								"'" + beanName + "' depends on missing bean '" + dep + "'", ex); }}}// Create 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);
						throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }else if (mbd.isPrototype()) {
				// It's a prototype -> create a new instance.
				Object prototypeInstance = null;
				try {
					beforePrototypeCreation(beanName);
					prototypeInstance = createBean(beanName, mbd, args);
				}
				finally {
					afterPrototypeCreation(beanName);
				}
				bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
			}

			else {
				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);
			throwex; }}// Check if required type matches the type of the actual bean instance.
	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 newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
}
Copy the code

As you can see from the sheer volume of code, loading beans goes through a fairly complex process that involves a variety of considerations.

Let’s summarize the above:

  • The conversion corresponds to beanName.
  • Try to load a singleton from the cache.
  • Instantiation of a bean.
  • Dependency checking for prototype patterns.
  • Testing parentBeanFactory.
  • Transform the GernericBeanDefinition that stores the XML configuration file to RootBeanDefinition.
  • Look for dependencies.
  • Create beans for different scopes.
  • Type conversion. (turn)

In fact, reading this, it is not difficult to see that it is not much different from our previous speculation, but the source developers have done more rigorous code logic, and do a lot of necessary verification.