[read the Spring source code like this] - go to the container Spring IOC again

Posted by lampquest on Mon, 07 Mar 2022 17:32:11 +0100

The preparatory work has been finished. Now we can officially enter the reading link. This article, like the title, mainly talks about the container, the core part of Spring. This word is not unfamiliar. Many people will come into contact with Spring's two core functions IOC and AOP when they come into contact with Spring. Here we read it from another angle, hoping to bring you some new cognition. Big guys, please skip. Please point out where you have objections.

What is a container

When it comes to containers, it is estimated that many people will come up with two concepts: IOC and DI.

  • IOC: Inversion of Control. IOC is not a technology, but a programming idea. What is control reversal and why? Let's start with a simple example:
public class Test {
    public static void main(String[] args) {
        new Test1().testMethod();
    }
}

class Test1 {
    public void testMethod() {
        System.out.println("testMethod Method called");
    }
}

This is a very simple code that calls the methods of another class in one class. When calling the method of another class, we directly create the object of that class. In the use of our framework, we do not need to create objects. It is the framework that gives us to do these things. Then we can clearly see a problem here. Objects are used in different ways.

Who controls who? In our above example, we can clearly see that the new object is controlled by our current method. But what about containers? We can hardly see the operation of new. We are all injected directly, so we can directly infer that the new action of the object is not the currently used program, but the framework. Specifically, the framework is doing this. What is forward rotation and reverse rotation? The so-called forward rotation refers to the direct new object within the program and assignment. Inversion is the container new object, which actively provides to the program

This operation solves a problem, decoupling. Try to think about it. If our code uses objects by writing new, the management of objects will be very complicated.

  • DI - Dependency Injection, i.e. "Dependency Injection": the dependency between components is determined by the container during the run-time. Figuratively speaking, the container dynamically injects a dependency into the component. The framework we mentioned above gives us new objects and then provides us with usable objects. In fact, this includes object injection. If not, how can the objects created by the framework be referenced by the code we write?

Application scenario of Spring IOC container

In Spring, Spring IoC provides a basic JavaBean container, which manages dependencies through IOC mode, and enhances the functions of transaction management and declaration cycle management for POJO objects such as JavaBeans through dependency injection and AOP aspects. In fact, if we regard IOC as a bucket, the beans in the project are all water. Without a framework, it is difficult to collect and retain water, but when we have this container, we find that it brings us great convenience. We don't need to go back and forth to new or care about how much it has.

Implementation of Spring IOC container

At present, there are two mainstream implementations of Spring IOC, BeanFactory and ApplicationContext. We might as well experience the two implementations.

  • BeanFactory needs to create a simple Spring project and introduce the basic dependencies of Spring. Then let's try to use the container to create a simple bean first
public class TestBean {

    private String name = "this is bean";

    public String getName() {
        return this.name;
    }

}

There is nothing special about this bean. It is very common. Then we will create a configuration file testbeanfactory XML and hand over the bean to the container for management

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="testBean" class="com.echo.demo.TestBean"/>

</beans>

In our ordinary program, if we want to use beans, we need to go to new, but here, we don't need it. You can get beans directly through the container

Gets the bean managed by the container

package com.echo.demo;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class Test {

    public static void main(String[] args) {

        XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("testBeanFactory.xml"));
        TestBean testBean = (TestBean)xmlBeanFactory.getBean("testBean");
        System.out.println(testBean.getName());

    }

}

So far, we have experienced the basic usage of BeanFactory. Very simply, we just built a bean and declared it, and then we can get the instance of the bean.

  • ApplicationContext we might as well use ApplicationContext as an example to compare. Same as above. Same bean
public class TestBean {

    private String name = "this is bean";

    public String getName() {
        return this.name;
    }

}

Note that the implementation of ApplicationContext also requires xml files

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="testBean" class="com.echo.demo.TestBean" />

</beans>

So far, in fact, there is no difference between the two methods we have seen, but we can see from the code for obtaining beans that the two lines of code for obtaining beans are different

package com.echo.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testApplicationContext.xml");
        TestBean testBean = (TestBean) applicationContext.getBean("testBean");
        System.out.println(testBean.getName());
    }

}

In fact, from the perspective of our use, these two simple codes are not substantially different. They just use another class to call the method of obtaining bean s. These two pieces of code, whether from the surface or from its implementation, can not escape such a few:

  • Read configuration file
  • Find the corresponding class according to the configuration file and instantiate it through reflection
  • Then it is stored in the container and taken out when called

Is that really the case? Let's go deeper and review our ideas

Here we have guesses and code examples. We might as well have a deep understanding through the sequence diagram. This sequence diagram starts from the Test class of our code. Here we draw a sequence diagram of XmlBeanFactory initialization to see what our code logic has done.

When we see this sequence diagram, we should be able to clearly know what our code has done. The code starts from getting the file from ClassPathResource and finally gets the object. If we only look at the timing chart, it is not very different from our previous speculation. In order to further understand our speculation, we use the source code to correct it.

  • What exactly did ClassPathResource do in the code? Look here first
/**
 * Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
 * A leading slash will be removed, as the ClassLoader resource access
 * methods will not accept it.
 * <p>The thread context class loader will be used for
 * loading the resource.
 * @param path the absolute path within the class path
 * @see java.lang.ClassLoader#getResourceAsStream(String)
 * @see org.springframework.util.ClassUtils#getDefaultClassLoader()
 */
public ClassPathResource(String path) {
	this(path, (ClassLoader) null);
}

/**
 * Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
 * A leading slash will be removed, as the ClassLoader resource access
 * methods will not accept it.
 * @param path the absolute path within the classpath
 * @param classLoader the class loader to load the resource with,
 * or {@code null} for the thread context class loader
 * @see ClassLoader#getResourceAsStream(String)
 */
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
	Assert.notNull(path, "Path must not be null");
	String pathToUse = StringUtils.cleanPath(path);
	if (pathToUse.startsWith("/")) {
		pathToUse = pathToUse.substring(1);
	}
	this.path = pathToUse;
	this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}


/**
 * Return the default ClassLoader to use: typically the thread context
 * ClassLoader, if available; the ClassLoader that loaded the ClassUtils
 * class will be used as fallback.
 * <p>Call this method if you intend to use the thread context ClassLoader
 * in a scenario where you clearly prefer a non-null ClassLoader reference:
 * for example, for class path resource loading (but not necessarily for
 * {@code Class.forName}, which accepts a {@code null} ClassLoader
 * reference as well).
 * @return the default ClassLoader (only {@code null} if even the system
 * ClassLoader isn't accessible)
 * @see Thread#getContextClassLoader()
 * @see ClassLoader#getSystemClassLoader()
 */
@Nullable
public static ClassLoader getDefaultClassLoader() {
	ClassLoader cl = null;
	try {
		cl = Thread.currentThread().getContextClassLoader();
	}
	catch (Throwable ex) {
		// Cannot access thread context ClassLoader - falling back...
	}
	if (cl == null) {
		// No thread context class loader -> use class loader of this class.
		cl = ClassUtils.class.getClassLoader();
		if (cl == null) {
			// getClassLoader() returning null indicates the bootstrap ClassLoader
			try {
				cl = ClassLoader.getSystemClassLoader();
			}
			catch (Throwable ex) {
				// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
			}
		}
	}
	return cl;
}


/**
 * Returns the system class loader for delegation.  This is the default
 * delegation parent for new <tt>ClassLoader</tt> instances, and is
 * typically the class loader used to start the application.
 *
 * <p> This method is first invoked early in the runtime's startup
 * sequence, at which point it creates the system class loader and sets it
 * as the context class loader of the invoking <tt>Thread</tt>.
 *
 * <p> The default system class loader is an implementation-dependent
 * instance of this class.
 *
 * <p> If the system property "<tt>java.system.class.loader</tt>" is defined
 * when this method is first invoked then the value of that property is
 * taken to be the name of a class that will be returned as the system
 * class loader.  The class is loaded using the default system class loader
 * and must define a public constructor that takes a single parameter of
 * type <tt>ClassLoader</tt> which is used as the delegation parent.  An
 * instance is then created using this constructor with the default system
 * class loader as the parameter.  The resulting class loader is defined
 * to be the system class loader.
 *
 * <p> If a security manager is present, and the invoker's class loader is
 * not <tt>null</tt> and the invoker's class loader is not the same as or
 * an ancestor of the system class loader, then this method invokes the
 * security manager's {@link
 * SecurityManager#checkPermission(java.security.Permission)
 * <tt>checkPermission</tt>} method with a {@link
 * RuntimePermission#RuntimePermission(String)
 * <tt>RuntimePermission("getClassLoader")</tt>} permission to verify
 * access to the system class loader.  If not, a
 * <tt>SecurityException</tt> will be thrown.  </p>
 *
 * @return  The system <tt>ClassLoader</tt> for delegation, or
 *          <tt>null</tt> if none
 *
 * @throws  SecurityException
 *          If a security manager exists and its <tt>checkPermission</tt>
 *          method doesn't allow access to the system class loader.
 *
 * @throws  IllegalStateException
 *          If invoked recursively during the construction of the class
 *          loader specified by the "<tt>java.system.class.loader</tt>"
 *          property.
 *
 * @throws  Error
 *          If the system property "<tt>java.system.class.loader</tt>"
 *          is defined but the named class could not be loaded, the
 *          provider class does not define the required constructor, or an
 *          exception is thrown by that constructor when it is invoked. The
 *          underlying cause of the error can be retrieved via the
 *          {@link Throwable#getCause()} method.
 *
 * @revised  1.4
 */
@CallerSensitive
public static ClassLoader getSystemClassLoader() {
    initSystemClassLoader();
    if (scl == null) {
        return null;
    }
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkClassLoaderPermission(scl, Reflection.getCallerClass());
    }
    return scl;
}

Basically, the core code has been taken out, which is not difficult to find. In fact, it has done some initialization and made a judgment of permission. But the code doesn't seem to get the file at this step? Didn't get the real content of the file? Because this article is too long, we have finished the preparatory work for the next article. Now we can officially enter the reading link. This article, like the title, mainly talks about the container, the core part of Spring. This word is not unfamiliar. Many people will come into contact with Spring's two core functions IOC and AOP when they come into contact with Spring. Here we read it from another angle, hoping to bring you some new cognition. Big guys, please skip. Please point out where you have objections.

###What is a container
When it comes to containers, it is estimated that many people will come up with two concepts: IOC and DI.
-IOC: Inversion of Control.
IOC is not a technology, but a programming idea. What is control reversal and why?
Let's start with a simple example:

public class Test {  
    public static void main(String\[\] args) {  
        new Test1().testMethod();  
    }  
}

class Test1 {  
    public void testMethod() {  
        System.out.println("testMethod Method called");  
    }  
}  

This is a very simple code that calls the methods of another class in one class. When calling the method of another class, we directly create the object of that class. In the use of our framework, we do not need to create objects. It is the framework that gives us to do these things. Then we can clearly see a problem here. Objects are used in different ways.

Who controls who? In our above example, we can clearly see that the new object is controlled by our current method. But what about containers? We can hardly see the operation of new. We are all injected directly, so we can directly infer that the new action of the object is not the currently used program, but the framework. Specifically, the framework is doing this.
What is forward rotation and reverse rotation? The so-called forward rotation refers to the direct new object within the program and assignment. Inversion is the container new object, which actively provides to the program
>This operation solves a problem, decoupling. Try to think about it. If our code uses objects by writing new, the management of objects will be very complicated.

- DI—Dependency Injection
Namely "dependency injection": the dependency between components is determined by the container during the run-time. Figuratively speaking, the container dynamically injects a dependency into the component. The framework we mentioned above gives us new objects and then provides us with usable objects. In fact, this includes object injection. If not, how can the objects created by the framework be referenced by the code we write?

###Application scenario of Spring IOC container
In Spring, Spring IoC provides a basic JavaBean container, which manages dependencies through IOC mode, and enhances the functions of transaction management and declaration cycle management for POJO objects such as JavaBeans through dependency injection and AOP aspects. In fact, if we regard IOC as a bucket, the beans in the project are all water. Without a framework, it is difficult to collect and retain water, but when we have this container, we find that it brings us great convenience. We don't need to go back and forth to new or care about how much it has.

###Implementation of Spring IOC container
At present, there are two mainstream implementations of Spring IOC, BeanFactory and ApplicationContext. We might as well experience the two implementations.
- BeanFactory
We need to create a Spring dependent project, and then we need to use Spring to try
First, create a simple bean

public class TestBean {

    private String name = "this is bean";

    public String getName() {  
        return this.name;  
    }

}  

There is nothing special about this bean. It is very common. Then we will create a configuration file testbeanfactory XML and hand over the bean to the container for management

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="testBean" class="com.echo.demo.TestBean"/>

</beans>  

>In our ordinary program, if we want to use beans, we need to go to new, but here, we don't need it. You can get beans directly through the container

Gets the bean managed by the container

package com.echo.demo;

import org.springframework.beans.factory.xml.XmlBeanFactory;  
import org.springframework.core.io.ClassPathResource;

public class Test {

    public static void main(String\[\] args) {

        XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("testBeanFactory.xml"));  
        TestBean testBean = (TestBean)xmlBeanFactory.getBean("testBean");  
        System.out.println(testBean.getName());

    }

}  

>So far, we have experienced the basic usage of BeanFactory. Very simply, we just built a bean and declared it, and then we can get the instance of the bean.

- ApplicationContext
We might as well use ApplicationContext as an example to compare. Same as above. Same bean

public class TestBean {

    private String name = "this is bean";

    public String getName() {  
        return this.name;  
    }

}  

Note that the implementation of ApplicationContext also requires xml files

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="testBean" class="com.echo.demo.TestBean" />

</beans>  

So far, in fact, there is no difference between the two methods we have seen, but we can see from the code for obtaining beans that the two lines of code for obtaining beans are different

package com.echo.demo;

import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {

    public static void main(String\[\] args) {  
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("testApplicationContext.xml");  
        TestBean testBean = (TestBean) applicationContext.getBean("testBean");  
        System.out.println(testBean.getName());  
    }

}  

In fact, from the perspective of our use, these two simple codes are not substantially different. They just use another class to call the method of obtaining bean s. These two pieces of code, whether from the surface or from its implementation, can not escape such a few:
-Read configuration file
-Find the corresponding class according to the configuration file and instantiate it through reflection
-Then it is stored in the container and taken out when called

>Is that really the case? Let's go deeper and review our ideas

Here we have guesses and code examples. We might as well have a deep understanding through the sequence diagram. This sequence diagram starts from the Test class of our code. Here we draw a sequence diagram of XmlBeanFactory initialization to see what our code logic has done.
! [insert picture description here]( https://img-blog.csdnimg.cn/20210413140551612.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hsZWNobw==,size_16,color_FFFFFF,t_70)

When we see this sequence diagram, we should be able to clearly know what our code has done. The code starts from getting the file from ClassPathResource and finally gets the object. If we only look at the timing chart, it is not very different from our previous speculation. In order to further understand our speculation, we use the source code to correct it.

-What exactly did ClassPathResource do in the code? Look here first

/**  
 \* Create a new {@code ClassPathResource} for {@code ClassLoader} usage.  
 \* A leading slash will be removed, as the ClassLoader resource access  
 \* methods will not accept it.  
 \* <p>The thread context class loader will be used for  
 \* loading the resource.  
 \* @param path the absolute path within the class path  
 \* @see java.lang.ClassLoader#getResourceAsStream(String)  
 \* @see org.springframework.util.ClassUtils#getDefaultClassLoader()  
 */  
public ClassPathResource(String path) {  
    this(path, (ClassLoader) null);  
}

/**  
 \* Create a new {@code ClassPathResource} for {@code ClassLoader} usage.  
 \* A leading slash will be removed, as the ClassLoader resource access  
 \* methods will not accept it.  
 \* @param path the absolute path within the classpath  
 \* @param classLoader the class loader to load the resource with,  
 \* or {@code null} for the thread context class loader  
 \* @see ClassLoader#getResourceAsStream(String)  
 */  
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {  
    Assert.notNull(path, "Path must not be null");  
    String pathToUse = StringUtils.cleanPath(path);  
    if (pathToUse.startsWith("/")) {  
        pathToUse = pathToUse.substring(1);  
    }  
    this.path = pathToUse;  
    this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());  
}

  
/**  
 \* Return the default ClassLoader to use: typically the thread context  
 \* ClassLoader, if available; the ClassLoader that loaded the ClassUtils  
 \* class will be used as fallback.  
 \* <p>Call this method if you intend to use the thread context ClassLoader  
 \* in a scenario where you clearly prefer a non-null ClassLoader reference:  
 \* for example, for class path resource loading (but not necessarily for  
 \* {@code Class.forName}, which accepts a {@code null} ClassLoader  
 \* reference as well).  
 \* @return the default ClassLoader (only {@code null} if even the system  
 \* ClassLoader isn't accessible)  
 \* @see Thread#getContextClassLoader()  
 \* @see ClassLoader#getSystemClassLoader()  
 */  
@Nullable  
public static ClassLoader getDefaultClassLoader() {  
    ClassLoader cl = null;  
    try {  
        cl = Thread.currentThread().getContextClassLoader();  
    }  
    catch (Throwable ex) {  
        // Cannot access thread context ClassLoader - falling back...  
    }  
    if (cl == null) {  
        // No thread context class loader -> use class loader of this class.  
        cl = ClassUtils.class.getClassLoader();  
        if (cl == null) {  
            // getClassLoader() returning null indicates the bootstrap ClassLoader  
            try {  
                cl = ClassLoader.getSystemClassLoader();  
            }  
            catch (Throwable ex) {  
                // Cannot access system ClassLoader - oh well, maybe the caller can live with null...  
            }  
        }  
    }  
    return cl;  
}

  
/**  
 \* Returns the system class loader for delegation.  This is the default  
 \* delegation parent for new <tt>ClassLoader</tt> instances, and is  
 \* typically the class loader used to start the application.  
 *  
 \* <p> This method is first invoked early in the runtime's startup  
 \* sequence, at which point it creates the system class loader and sets it  
 \* as the context class loader of the invoking <tt>Thread</tt>.  
 *  
 \* <p> The default system class loader is an implementation-dependent  
 \* instance of this class.  
 *  
 \* <p> If the system property "<tt>java.system.class.loader</tt>" is defined  
 \* when this method is first invoked then the value of that property is  
 \* taken to be the name of a class that will be returned as the system  
 \* class loader.  The class is loaded using the default system class loader  
 \* and must define a public constructor that takes a single parameter of  
 \* type <tt>ClassLoader</tt> which is used as the delegation parent.  An  
 \* instance is then created using this constructor with the default system  
 \* class loader as the parameter.  The resulting class loader is defined  
 \* to be the system class loader.  
 *  
 \* <p> If a security manager is present, and the invoker's class loader is  
 \* not <tt>null</tt> and the invoker's class loader is not the same as or  
 \* an ancestor of the system class loader, then this method invokes the  
 \* security manager's {@link  
 \* SecurityManager#checkPermission(java.security.Permission)  
 \* <tt>checkPermission</tt>} method with a {@link  
 \* RuntimePermission#RuntimePermission(String)  
 \* <tt>RuntimePermission("getClassLoader")</tt>} permission to verify  
 \* access to the system class loader.  If not, a  
 \* <tt>SecurityException</tt> will be thrown.  </p>  
 *  
 \* @return  The system <tt>ClassLoader</tt> for delegation, or  
 \*          <tt>null</tt> if none  
 *  
 \* @throws  SecurityException  
 \*          If a security manager exists and its <tt>checkPermission</tt>  
 \*          method doesn't allow access to the system class loader.  
 *  
 \* @throws  IllegalStateException  
 \*          If invoked recursively during the construction of the class  
 \*          loader specified by the "<tt>java.system.class.loader</tt>"  
 \*          property.  
 *  
 \* @throws  Error  
 \*          If the system property "<tt>java.system.class.loader</tt>"  
 \*          is defined but the named class could not be loaded, the  
 \*          provider class does not define the required constructor, or an  
 \*          exception is thrown by that constructor when it is invoked. The  
 \*          underlying cause of the error can be retrieved via the  
 \*          {@link Throwable#getCause()} method.  
 *  
 \* @revised  1.4  
 */  
@CallerSensitive  
public static ClassLoader getSystemClassLoader() {  
    initSystemClassLoader();  
    if (scl == null) {  
        return null;  
    }  
    SecurityManager sm = System.getSecurityManager();  
    if (sm != null) {  
        checkClassLoaderPermission(scl, Reflection.getCallerClass());  
    }  
    return scl;  
}  

Basically, the core code has been taken out, which is not difficult to find. In fact, it has done some initialization and made a judgment of permission. But the code doesn't seem to get the file at this step? Didn't get the real content of the file? Because this article is too long, we will analyze this problem in the next article

Topics: Java