Now that the preparation is done, we can begin the reading in earnest. The core part of Spring is the container. The term is a familiar one, and many people will come across the two core features of Spring: IOC and AOP. Let’s read it from a different Angle, and hopefully give you some new insight. Big guy please skip over, the place that has objection still asks everybody big guy to point out.
What is a container
When it comes to containers, two concepts come to mind: IOC and DI.
- It’s an IOC Inversion of Control.
IOC is not a technology, but a programming idea. So what is an inversion of control and why is an inversion of control? Let’s start with a simple example:
public class Test {
public static void main(String[] args) {
newTest1().testMethod(); }}class Test1 {
public void testMethod(a) {
System.out.println("TestMethod method called"); }}Copy the code
This is a very simple code, in one class called another class method. When we call a method of another class, we create the object of that class directly. In our use of the framework, we don’t need to create objects; the framework does it for us. There is an obvious problem with objects being used in different ways.
Who controls whom? In our example above, it is obvious that the new object is controlled by our current method. But what about containers? We can hardly see the new operation. We use it directly, so we can infer that the new action of the object is not the currently used program, but the framework. Specifically, the framework does this. What are forward and reverse? The so-called forward transformation, is the program directly new objects, and assign values. The inversion is the container new object, provided to the program voluntarily
This operation solves one problem, decoupling. Imagine if all of our code used objects by writing new, object management would be cumbersome.
- DI – Dependency Injection
Dependency injection: The dependencies between components are determined by the container at 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 objects that we can use. In fact, this includes object injection. If we do not inject, how can the object created by the framework be referenced by the code we write?
Application scenarios of the Spring IOC container
In Spring, Spring IoC provides a basic JavaBean container that manages dependencies through the IoC pattern and enhances transaction management, declaration cycle management, and other capabilities for POJO objects like JavaBean through dependency injection and AOP facets. In fact, if we think of IOC as a bucket, then the beans in the project are all water. Without a framework, water is difficult to collect and retain. But when we have this container, we find that it brings us great convenience. We do not need to go back and forth to New, nor do we need to care about how much water there is.
Implementation of the Spring IOC container
There are currently two main implementations of Spring IOC,BeanFactory and ApplicationContext. Let’s take a look at both implementations.
- BeanFactory
Here we need to create a simple Spring project, introduce Spring’s basic dependencies, and then try using the container to create a simple bean
public class TestBean {
private String name = "this is bean";
public String getName(a) {
return this.name; }}Copy the code
There is nothing special about this bean, it is ordinary. We then create a configuration file, testBeanFactory.xml, to hand the bean over to the container for management
<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>
Copy the code
In our normal program, we need to go to New to use beans, but here, we don’t need to. Beans can be obtained directly from the container
Gets container-managed beans
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()); }}Copy the code
So far, we have experienced the basic usage of the BeanFactory. It’s very simple. We just create a bean, declare it, and then get an instance of the bean.
- ApplicationContext
Let’s use the ApplicatonContext as an example. Same as above, same bean
public class TestBean {
private String name = "this is bean";
public String getName(a) {
return this.name; }}Copy the code
Note that the implementation of the ApplicatonContext also requires an XML file
<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>
Copy the code
So far, there is no difference between the two methods we have seen, but if we look at the code to get the bean, we can see that the two lines of code to get the bean 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()); }}Copy the code
These two simple pieces of code, in fact, from our point of view, there is no substantial difference, just use another class to call the bean method. These two pieces of code, both superficially and in terms of their implementation, cannot escape the following:
- Reading configuration Files
- Find the corresponding class based on the configuration file and instantiate it through reflection
- It is then stored in the container and retrieved when called
Is it really like this? Let’s go into the implementation and double-check our ideas
Now that we have the conjecture and code examples, let’s take a closer look at the sequence diagram. This sequence diagram starts with our code’s Test class, where we draw a sequence diagram of the XmlBeanFactory initialization to see what our code logic actually does.
When we look at this sequence diagram, it should be pretty clear what our code is actually doing. The code starts by getting the file from the ClassPathResource and eventually gets the object. If you look at it from the sequence diagram, it doesn’t look very different from what we guessed before. To further understand our prediction, we use the source code to correct it.
- What exactly does the ClassPathResource do in this code? See here first
/**
* Create a new {@code ClassPathResource} for {@codeClassLoader} 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(a) {
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.
*
* @throwsError * 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.
*
* @revised1.4 * /
@CallerSensitive
public static ClassLoader getSystemClassLoader(a) {
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if(sm ! =null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}
Copy the code
Basically the core code has been taken out, it is not difficult to find, in fact, is to do some initialization, but also made a judgment of permission. But the code doesn’t seem to fetch the file at this point, right? Didn’t get the actual contents of the file? Because this article is too long, we will analyze this question in the next article