First, the old rules, first comparison point unitary moth

As a back-end programmer who often uses Spring, Xiaobian has long wanted to thoroughly understand the whole Spring framework! But it is very large overall, all the inheritance diagram is very complex, plus xiaobian practice is still shallow, appears to be powerless. However, men should aspire to live, today let’s start from the initialization of Spring IOC container, even if we can not complete the complete mastery of the whole Spring framework, there is no shame, because xiaobian start, steady, we can win!

Here are some pre-reading tips:

  • 1, reading source code analysis is very boring, but now that you have come in, is certainly to understand this thing, also hope that this summary can inspire you.
  • In front of high energy, the article may be very long, illustrated.
  • 3. Before reading, it is recommended that you have an understanding of relevant design patterns and 6 principles of software design, which will be interspersed in the article.
  • 4, Xiaobian reading four years, knowledge is still shallow, like to study, if you find that the article views are wrong or different from your views, welcome to point out the comments and communication!
  • It is recommended that you do debugging tracking in the IDE while reading this article
  • 6. All UML diagrams in this paper are automatically generated using IDEA. The specific generation method is as follows:Select a class name, CTRL + Shift + Alt +U, CTRL + Alt +B, and press Enter

2. What will the article focus on?

Not much, just one line of code, as shown below:

This sentence is the Spring initialization code, although only one code, but the content thief!

What are the components of Spring container IOC?

In this way, xiaobian first clear the thought, step by step:

  • 1. The above code has a file calledapplicationContext.xmlThis is a resource file due to ourbeanAll configuration definitions are in there, so Spring has to do it in this fileRead and parse!!!! So Spring has a module calledResourceA module, as the name suggests, isresources! For all resourcesXML, TXT, and propertyFile resource abstraction. aboutResourceFor more information, please refer to the following two articles:

Let me talk about my understanding of Spring Resource

Analysis of Spring Resource Files and Application of Policy Patterns (Li Gang)

The following first paste a small make up generated class diagram (the picture is a little big, I do not know will not be clear, if not clear can be generated in accordance with the above idea generation method can be generated) :

You can see that Resource is the root interface of the whole system. If you click on the source code, you can see that it defines many policy methods, because it uses the design pattern of policy pattern. The advantage of using the policy interface/class is that the same policy is defined, and different subclasses have different specific policy implementations. When the client calls a specific implementation object such as UrlResource or FileSystemResource to the policy interface/class Resource can be!

All strategies are as follows:

  • 2. The Spring framework uses abstractions for various resourcesThe strategy patternNow that we have the resource, how do we load the resource? And then there’s the next oneResourceLoaderComponent that is responsible for loading Spring resources, which arexml,propertiesReturns a file resource of the corresponding typeResourceObject.. The UML diagram is as follows:

As you can see from the UML diagram above, the ResourceLoader component is similar to the Resource component, which is a root interface and should be implemented in different subclasses, such as loading resources from the file system, You can use FileSystemResourceLoader, load resources from the ServletContext context, you can use ServletContextResourceLoader. And most important, see from above, ApplicationContext, AbstractApplication is realized ResourceLoader, that means what? Our ApplicationContext, ApplicationContext, has the ability to load resources, It is also why can by passing in a String resource path to ClassPathXmlApplicationContext (” the applicationcontext.xml “) can get the cause of the XML file resources! Is that clear? Nice!

  • 3, the above two points, good! Now that we have the loaderResourceLoaderYou have a description of the resourceResource, but we declare it in the XML file<bean/>How is the tag represented in Spring? Notice this is just saying yesbeanDefinition, rather than saying how will<bean/>convertbeanObject. I think it should be easy to understand! It’s like you want to represent a studentStudentThen you must declare a class in your programStudent!!!! As for the student data fromexcelImport, or program runtimenewOut, or outxmlAll that matters is that you have something that represents real entities as objects in your program, so<bean/>You also need to make a definition in Spring! So we introduce a guy calledBeanDefinitionThe UML diagram is as follows:

Here’s a UML diagram:

First, the
tag in the configuration file corresponds to our BeanDefinition. The

element tag has class, scope, lazy-init, and so on. BeanDefinition provides the corresponding beanClass, Scope, and lazyInit attributes.

Among them, RootBeanDefinition is the most commonly used implementation class, which corresponds to the general

element tag. GenericBeanDefinition is a newly added bean file configuration attribute definition class since 2.5, and it is a one-stop service class. = RootBeanDefinition = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton = ChildBeanDefiniton A

without a parent

is represented by RootBeanDefinition. AbstractBeanDefinition abstracts the common class information of both. Spring converts the

configuration information in the configuration file into the internal representation of the container through BeanDefinition and registers these BeanDefinitonRegistry. The BeanDefinitionRegistry of the Spring container acts as an in-memory database for Spring configuration information, mainly in the form of a map, and then reads configuration information directly from the BeanDefinitionRegistry. In general, BeanDefinition is only loaded and parsed when the container is started. This information does not change unless the container is refreshed or restarted, although users can also adjust the definition at run time by programming if they have special needs.

  • 4, with the loaderResourceLoaderYou have a description of the resourceResource, also have the rightbeanBy definition, we can’t help but ask ourResourceHow do resources turn into usBeanDefinition? So it’s introducedBeanDefinitionReaderComponent,Reader! Is a read mechanism, UML diagram is as follows:

As you can see, Spring abstracts Reader and gives it to its subclasses to implement the specific functions, and different implementations are corresponding to different classes. Such as PropertiedBeanDefinitionReader XmlBeanDefinitionReader that from the Resource Property and XML parsing into BeanDefinition.

For example, Dom4j parser SAXReader = new SAXReader(); SAXReader = new SAXReader(); Document doc = reader.read(url.getFile()); // The url is an URLResource object.

  • 5. All right! Basically all components are almost complete! Oh, there’s one more component. You have itBeanDefinitionAfter that, you also have to register them in the factory, so when you usegetBean()Method until the factory knows what to return to you. One more problem, since we’re going to save these registrationsbeanThere must be a data structure to act as the container. Yes, it isMap“And posted belowBeanDefinitionRegistryAn implementation of, calledSimpleBeanDefinitionRegistrySource diagram of:

The UML diagram for BeanDefinitionRegistry looks like this:

Can be seen from the graph, there are three default implementation BeanDefinitionRegistry, SimpleBeanDefinitionRegistry, DefaultListableBeanFactory, GenericApplicationContext which SimpleBeanDefinitionRegistry, DefaultListableBeanFactory have a Map, that is to say the two preserved the bean implementation class. While GenericApplicationContext holds a DefaultListableBeanFactory object references used to capture inside the corresponding Map. In DefaultListableBeanFactory

In GenericApplicationContext

  • 6. You can basically see the five points mentioned aboveApplicationContextContext basically runs through all parts directly or indirectly, so we generally call itThe containerIn addition to that,ApplicationContextIn addition toThe bean containerIn addition to this role, it also includes obtaining the environment parameters of the entire program (such as JDK version, JRE, etc.). In fact, this part of Spring has also done the corresponding encapsulation, calledEnviroment, the following follow the small series of Eclipse, together with the debug container initialization project!

Practice is the sole criterion for testing truth

The student.java class is as follows:

package com.wokao666;

public class Student {

	private int id;
	private String name;
	private int age;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Student(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}

	public Student() {
		super();
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; }}Copy the code

This is configured in application.xml, with two beans:

<bean id="stu1" class="com.wokao666.Student">
 	<property name="id" value="1"></property>
 	<property name="name" value="xiaoming"></property>
 	<property name="age" value="21"></property>
 </bean>
  <bean id="stu2" class="com.wokao666.Student">
 	<property name="id" value="2"></property>
 	<property name="name" value="xiaowang"></property>
 	<property name="age" value="22"></property>
 </bean>
Copy the code

Ok, let’s make a Breakpoint for the first piece of code:

The first step:The ContextClosedEvent class is eagerly loaded to avoid strange classloader problems when the application closes in WebLogic 8.1.

Don’t worry too much about this step!

The second step:Since this is new ClassPathXmlApplicationContext ()Just call the constructor!

Step 3:

Step 4:

Good, we follow the third step of the super (parent), coupled with the 6 dots above the third quarter, the UML diagrams, step by step tracking, then we came to AbstractApplicationContext this method:

What is the type of the resourcePatternResolver inside? Which part of the 6 steps in section 3? It is of type ResourcePatternResolver. ResourcePatternResolver inherits the ResourceLoader interface. Therefore, it belongs to the load resource module. Let’s take a look at the source of ResourcePatternResolver, as shown below:

There you are! Not only does the ResourceLoader interface inherit, but only a getResources() method is defined to return the Resource[] collection of resources. Furthermore, this interface also uses the policy pattern, whose concrete implementation is in the implementation class, ok! Just take a look at the UML diagram!

PathMatchingResourcePatternResolver this implementation class! It is used to interpret different path resources. For example, the resource path you pass in May be a regular URL, or it may be prefixed with classpath*.

ServletContextResourcePatternResolver the implementation class as its name implies is to load the Servlet context, usually used in the web.

Step 5:

Then the fourth step method, we are not enter the methods of the fourth step, at this point to instantiate AbstractApplicationContext, some of the qualities of the this object be initialized at this time (such as log object), the following figure:

Then enter the getResourcePatternResolver () method:

The fourth step that PathMatchingResourcePatternResolver for processing different resource path, how to deal with, we advanced to see see!

If so, the console prints the Equinox FileLocator log found for OSGi package URL resolution. It’s not printed, obviously not found!

Return the setParent() method.

Step 6:

If the parent is not null, then the parent is merged with the current this application context. Obviously nothing is being done! Parent is null, so no merge! Still use the current this environment.

To sum up, the first six steps basically do two things:

  • 1. Initialize the relevant context, i.e., initializationClassPathXmlApplicationContextThe instance
  • 2. Get oneresourcePatternResolverObject to facilitate the resolution of the resources in step 7Resourceobject

Step 7:

Step 7 returns to the code we started with in step 3, since we have finished tracking super(parent) in the previous 6 steps. Let’s see how setConfigLocation() works

        /**
	 * Set the config locations for* <p>If not. * <p>If notset, the implementation may use a default as appropriate.// If not set, the implementation may use a default as appropriate. */ public voidsetConfigLocations(String... locations) {
		if(locations ! = null) {// Assert. NoNullElements (locations,"Config locations must not be null"); Null this.configLocations = new String[locations. Length];for(int i = 0; i < locations.length; i++) { this.configLocations[i] = resolvePath(locations[i]).trim(); // Go to the space, curious what resolvePath does? }}else{ this.configLocations = null; }}Copy the code

Go to the resolvePath() method and see:

/** * resolves the given resource path, replacing placeholders with the corresponding environment attribute values, if necessary, to apply to the resource path configuration. * Resolve the given path, replacing placeholders with corresponding * environment property valuesif necessary. Applied to config locations.
	 * @param path the original file path
	 * @return the resolved file path
	 * @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)
	 */
	protected String resolvePath(String path) {
		return getEnvironment().resolveRequiredPlaceholders(path);
	}
Copy the code

Go to getEnvironment() and have a look:

	/**
	 * {@inheritDoc}
	 * <p>If {@code null}, a new environment will be initialized via
	 * {@link #createEnvironment()}.
	 */
	@Override
	public ConfigurableEnvironment getEnvironment() {
		if (this.environment == null) {
			this.environment = createEnvironment();
		}
		return this.environment;
	}
Copy the code

Going into the createEnvironment() method, we see that here we create a new StandardEnviroment object, which is an implementation class for Environment, representing the Environment in which the container runs, such as the JDK Environment, Servlet Environment, Spring Environment, and so on. Each environment has its own configuration data, such as system.getProperties (), system.getenv () can get JDK environment data; ServletContext. GetInitParameter () can get the Servlet Environment configuration data, etc., that is to say the Spring to abstract a Environment Environment configuration.

The resulting StandardEnviroment object doesn’t contain anything, just a standard environment, with all the attributes being default values.

Summary: Path resolution is performed on the passed path

Step 8:This step is the most important

A summary: So far, we have the following examples:

Now the code runs to the refresh() method as shown below:

Let’s see what this method says. Okay?

@Override public void refresh() throws BeansException, An IllegalStateException {synchronized (enclosing startupShutdownMonitor) {/ / refresh the preparation work, including setting the startup time, whether the activation identifier, initialization properties source (the propertysource) configuration prepareRefresh (); / / create the beanFactory (process is based on the XML generated for each bean BeanDefinition and register to generate the beanFactory ConfigurableListableBeanFactory the beanFactory = obtainFreshBeanFactory(); // Prepare the beanFactory (set the ClassLoader for the beanFactory, set the SpEL expression parser, set the type converter to convert the XML String to the corresponding object), / / add built-in ApplicationContextAwareProcessor object, ignore the various Aware object, registered the account reconciliation for all kinds of built-in objects, such as the BeanFactory, ApplicationContext 】 【 / / registered AOP related things, PrepareBeanFactory (beanFactory); Try {// template method, The postProcessBeanFactory method of the BeanFactoryPostProcessor interface is postProcessBeanFactory(beanFactory). / / call all spring BeanFactoryPostProcessor registered as Bean invokeBeanFactoryPostProcessors (the beanFactory); / / registered all implements the BeanPostProcessor interface of Bean registerBeanPostProcessors (the beanFactory); // Initialize MessageSource, which is related to internationalization initMessageSource(); / / initialize the container events spread initApplicationEventMulticaster (); // Call the container subclass to initialize some special beans, template method onRefresh(); // Register the event propagators registerListeners(); / / initialize all the rest of the bean (common bean) finishBeanFactoryInitialization (the beanFactory); // Initialize the container's lifecycle event handler and publish the container's lifecycle event finishRefresh(); } catch (BeansException ex) {if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: "+ ex); } // Destroy the created bean destroyBeans(); // Reset the 'active' flag cancelRefresh(ex); throw ex; } finally {// resetCommonCaches(); }}}Copy the code

The Refresh() method is actually a template method. Many of the methods are implemented by different implementation classes, but the class implements some of them itself, and the methods that have been implemented are not allowed to be overwritten by subclasses, such as the prepareRefresh() method. For more on the Template approach design pattern, see my previous article about my understanding of the Template approach design pattern.

Go into the prepareRefresh() method first:

    /**
	 * Prepare this context for refreshing, setting its startup date and
	 * active flag as well as performing any initialization of property sources.
	 */
	protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); // Set the container startup time this.closed. Set ()false); // Container close flag, whether to close? this.active.set(true); // The container activation flag, whether to activate?if(logger.isInfoEnabled()) {// At this point, the console will print information about the current container logger.info("Refreshing "+ this); } // An empty method, overridden by a subclass, initializes the property file initPropertySources() in the container context; / / verification is marked as required all attributes are parsed, please refer to the ConfigurablePropertyResolver#setRequiredProperties getEnvironment().validateRequiredProperties(); // Allow early ApplicationEvents to be collected and published once the multicaster is available... this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>(); }Copy the code

Console output:

On March 22, 2018 afternoon 4:21:13 org. Springframework. Context. Support. The ClassPathXmlApplicationContext prepareRefresh information: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@96532d6: startup date [Thu Mar 22 16:21:09 CST 2018]; root of context hierarchyCopy the code

Step 9:

To enter obtainFreshBeanFactory() :

/ * * * tell subclass refresh internal bean plant (subclasses refers to AbstractApplicationContext subclass, We are using the ClassPathXmlApplicationContext) * Tell the ttf_subclass to refresh the internal bean factory. * / protected ConfigurableListableBeanFactoryobtainFreshBeanFactory() { refreshBeanFactory(); / / refresh Bean factory, if there have been a Bean plant, then shut down and destroyed, and then create a new Bean factory ConfigurableListableBeanFactory the beanFactory = getBeanFactory (); // Get the newly created Bean factoryif (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ":"+ beanFactory); // Console print}return beanFactory;
	}
Copy the code

Enter the refreshBeanFactory() method:

/** * This implementation performs the actual refresh of the context's base Bean factory, closing the previous Bean factory (if any) and initializing the fresh Bean factory for the next phase of the context's lifecycle. * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle.
	 */
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		if(hasBeanFactory()) {// destroyBeans() if there is already a bean factory; / / destroyed closeBeanFactory (); / / close} try {DefaultListableBeanFactory the beanFactory = createBeanFactory (); / / create a new bean plants. The beanFactory setSerializationId (getId ()); // Specify an ID for serialization purposes, from which the BeanFactory can be deserialized back to the BeanFactory object if desired. // customizeBeanFactory(beanFactory); // customizeBeanFactory(beanFactory); Beandefinitions (beanFactory) : (beanDefinitions (beanFactory)); (beanDefinitions (beanFactory)); (beanDefinitions (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

Conclusion: The main thing to do in this step is to determine whether the beanFactory existed before the container was refreshed. If so, destroy the old BeanFactory. Then destroy it and create a new BeanFactory to return to the container. At the same time, register the BeanDefinition of the XML file in the BeanFactory. If you’re not sure, go back to section 3, point 5

Step 10:

Go to Step 9 and take a look in the loadBeanDefinitions(beanFactory) method:

/** * load beandefnition with XmlBeanDefinitionReader; Loads the bean definition via an XmlBeanDefinitionReader. * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see#initBeanDefinitionReader
	 * @see #loadBeanDefinitions*/ @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// create XmlBeanDefinitionReader object XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Define beanDefinitionReader using Resource (S) for Enviroment (s); beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Initialize this reader. InitBeanDefinitionReader (beanDefinitionReader); LoadBeanDefinitions (DefinitionReader); // Define beanDefinitions (DefinitionReader); // Define beanDefinitions (DefinitionReader); }Copy the code

Console output:

On March 22, 2018 afternoon 5:09:40 org. Springframework. Beans. Factory. XML. XmlBeanDefinitionReader loadBeanDefinitions information: Loading XML bean definitions from class path resource [applicationContext.xml]Copy the code

Step 11:

PrepareBeanFactory (beanFactory) ¶

// set the bean classloader // set the Spring language expression (SpEL) parser // scan the ApplicationContextAware bean // register the classload time type aspect weaving (AOP) LoadTimeWeaver // Configure the default environment for the various beans loaded into the beanFactoryCopy the code

Step 12:

PostProcessBeanFactory (the beanFactory) method:

PostProcessBeanFactory is also a template method, and the subclass provides the implementation. Subclasses can have their own special post-processing methods on beanDefinitions, which are generated earlier, that is, the bean’s metadata. For example, modify the ID /name attribute, scope attribute, lazy-init attribute of a bean.

Step 13:

InvokeBeanFactoryPostProcessors (the beanFactory) method:

This method calls all BeanFactoryPostProcessor, which is an interface. Classes that implement this interface need to override the postProcessBeanFactory() method. More is provided to the developer to do processing on the generated BeanDefinition, by the developer to provide the processing logic.

Step 14:

The rest of the methods are basically like initializing the message processing source, initializing the container events, registering the bean listener to the event propagator, and finally refreshing the container.

Five, the summary

Congratulations to me, I’ve finished it, and congratulations to you, you’ve finished reading it.

I admire myself can spend so long to summarize the release, the reason to summarize, it is because small make up or agree that a good memory is not as good as a bad writing.

You do not remember, you will forget after a while, if you record, you will forget after a while! The difference lies in the forgotten, can go back in a very short time to recall, check the leak to fill the gap, reduce the cost of learning.

Moreover, I think my analysis is not perfect and there are many defects, so I publish all the articles I have written to discuss and communicate with you. The motto of Shantou University is very good, that is, knowledge should be shared, because shared knowledge can be a link between the past and the future.

Now let’s look at the Spring initialization process again:

  • 1, first initialize the context, generateClassPathXmlApplicationContextObject in the fetchresourcePatternResolverObject will bexmlParsed intoResourceObject.
  • 2. Use the context and resource generated by 1 to initialize the chemical plant, parse the resource into BeanDefinition, and register the BeanDefinition in BeanFactory.

Friends, find trouble, please comment and tell xiaobian, communicate together!