Applicant Ali, byte beating, Spring IOC and factory mode that Meituan must master

Spring IOC and Factory Mode

PS: The content of this article is hard core, which requires a better understanding of java's object-oriented, reflection, class loader, generics, properties, XML and other basic knowledge.

(1) Brief introduction

Before talking about Spring IOC, it's necessary to talk about Factory Pattern.Factory mode separates the caller of a Java object from the callee's implementation logic.Factory mode is one of the most common design modes in Java.This type of design pattern is creative and provides the best way to create objects.In factory mode, we do not expose creation logic to clients when creating objects and point to newly created objects by using a common interface.

This may be a bit abstract. Simply put, we won't need our own new object in the future. The instantiation of the object is handed over to the factory to complete. When we need the object, just ask the factory for one. We'll see an example later.It is important to note here that spring IOC is not exactly the same as factory mode. The biggest difference is whether you use new to create objects inside normal factory mode, but what is the benefit of spring IOC using reflection to create objects?

(2) "new" VS "reflection"

Let's look at an example of factory mode.For demonstration purposes, I'll write all the classes and interfaces together:

public interface Shape {
   void draw();}

public class Rectangle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }}

public class Square implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }}

public class Circle implements Shape {
   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }}

public class ShapeFactory {
   public Shape getShape(String shapeType){
      if(shapeType == null){
         return null;
      }        
      if(shapeType.equalsIgnoreCase("CIRCLE")){
         return new Circle();
      } else if(shapeType.equalsIgnoreCase("RECTANGLE")){
         return new Rectangle();
      } else if(shapeType.equalsIgnoreCase("SQUARE")){
         return new Square();
      }
      return null;
   }}

Let's see what this code does.Let's look at this ShapeFactory, which has a getShape method. Enter the name of a graphic and we'll get the object of that graphic.This is called a factory, and once we have this factory, what graphical objects we want to use can be obtained by calling the getShape method directly.Classes that use these objects can then be decoupled from these graphics classes.But it's easy to see that factories can now produce three different objects. If we add a new object to the factory, it's very cumbersome. If we modify the code and recompile it, it's just like a real factory suddenly wants to add a new production line.So we're definitely looking for improvements, and that's what gave birth to spring IOC.

The idea of spring IOC is basically the same as factory mode, except that the way objects are created changes from "new" to reflection, which gives you a lot of flexibility.However, it's too early to read the spring source code, so I wrote a simple example myself to simulate the basic principles of spring IOC.

First, if we want to create objects with reflection, the full class name is essential (review the reflection for friends who don't remember it), then we need a class name to tell the factory which object we need (just like the parameter shapeType passed in by the getShape method above), which is optional but can't be repeated.So we have two elements to create the object, and then we need a key-value pair to associate the two.Then a pattern is formed: we pass in the class name, the factory queries the key-value pair, finds the corresponding full class name, creates the object with reflection through the full class name, and returns it to us.Is it easy?

In a nutshell, let's create this key-value pair, the so-called configuration file. spring uses XML. I'll use properties for simplification. The principle is the same:

//File name: bean.propertiescircle=com.demo.Circle
rectangle=com.demo.Rectangle
square=com.demo.Square

Now that the configuration file is available, let's write about our BeanFactory.

//File name: BeanFactory.javapackage com.demo;import java.io.IOException;import java.io.InputStream;import java.util.Enumeration;import java.util.HashMap;import java.util.Map;import java.util.Properties;public class BeanFactory {
    //Configuration object (like the Bean Definition Registry in the spring IOC container)
    private static final Properties props;
    //A container that holds the created object and forms a key-value pair with the class name (like the Bean cache pool in the spring IOC container)
    private static Map<String, Object> beans;
    static {
        props = new Properties();
        //Read Configuration File through Class Loader
        InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        try {
            //Load profile to configuration object
            props.load(in);
            //Initialization Container
            beans = new HashMap<>();
            Enumeration<Object> keys = props.keys();
            //Loop through all class names (key) in the configuration object
            while (keys.hasMoreElements()){
                String key = keys.nextElement().toString();
                //Get the full class name (value) from the class name
                String beanPath = props.getProperty(key);
                //Creating objects with full class name reflection
                Object value = Class.forName(beanPath).getDeclaredConstructor().newInstance();
                //Place objects in containers
                beans.put(key, value);
            }
        } catch (IOException e) {
            throw new ExceptionInInitializerError("Initialization properties Failed!Program terminated!");
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    public static Object getBean(String beanName){
        //Getting objects from containers
        return beans.get(beanName);
    }}

The other three classes and one interface follow the same factory pattern as above, in which all java files are located under the com.demo package.Let's take a closer look at this BeanFactory (my own fake version, which mimics the basic functions of spring IOC). First, we grab the core. The core is Map <String, Object> beans, which directly marks the Bean Cache Pool in the spring IOC container to store the created objects. Map is used to get the corresponding objects directly by the class name.Then let's see how these objects are produced:

Object value = Class.forName(beanPath).getDeclaredConstructor().newInstance();

It is clear that reflection is in this sentence that we create objects by using the full class name of our class, which comes from our Proerties object, which is the object generated by reading our configuration file, defining a registry for the beans in the standard spring IOC container.Now that you know what this profile does, it's like a production order we give to a factory that says what we need to produce.Bean cache pools, on the other hand, are factory warehouses that store finished objects and wait to be removed.The Bean implementation classes we define (those Circle s, Square s, and so on) are equivalent to drawings that tell the factory what these objects look like and how they should be produced.Let's summarize:

Module function
Bean implementation class describes how to produce
Bean Defines the registry describing what needs to be produced
Bean Cache Pool (HashMap implementation) holds finished objects

(3) True Spring IOC

After reviewing the IOC I wrote, let's draw another picture to see the structure execution process of the real spring IOC, which is essentially the same as what I wrote.


Let's look at the implementation process:

1. Read Bean Configuration Information into Bean Definition Registry
2. Instantiate Beans from the Bean Registry
3. Put the instantiated bean instance into the Bean Cache Pool (HashMap implementation)
4. The application retrieves Bean instances from the Bean cache pool by class name
So many, let's see how to create objects using the spring IOC container.First, like my fake IOC above, we need to write an XML file first. XML is more complex than properties, but fortunately we don't have that complicated part yet.First go to the official website to find a template in the document and copy it:


<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd"></beans>

This is the template for the spring configuration file, which defines the constraints of some XML files, that is, what we can write in those XML tags.Our future development will be based on this template.Then, our class name and the key-value of the class name are correct, and these processes are exactly the same as above, but the writing is different, so let's write it all (note my writing).

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="circle" class="com.demo.Circle"/>
    <bean id="square" class="com.demo.Square"/>
    <bean id="rectangle" class="com.demo.Rectangle"/></beans>

Obviously, this id is equivalent to the class name, this class is equivalent to the full class name, is it basically the same as my Shanzhai version?

Of course, since we're working with frameworks, we don't need to do any of those tedious things, that is, we don't need to care about how objects are created and managed, which Spring does for us.All we have to do is ask the spring IOC container directly to take out the object.So where is this container?Now of course, we can only create this container manually, otherwise it won't come out empty, right.


Let's take a look at this interface inheritance diagram. We only need to focus on two of the top-level interfaces inside, BeanFactory and the bottom ApplicationContext.BeanFactory is a simple container that implements the basic functions of containers, such as getBean, containsBean, and so on.An ApplicationContext is an application context that adds context characteristics based on a simple container.We typically use the ApplicationContext interface when developing because it's more powerful than BeanFactory.Of course, the name "application context" may seem strange, but we just need to remember that it is the spring IOC container interface.Now that you have an interface, the next step is to find the implementation class.

There are many implementation classes for the ApplicationContext, so let's pick one of the most commonly used ones, ClassPathXmlApplicationContext.


Let's first take a look at his name: ClassPathXmlApplicationContext.Translated to: Classpath XML application context, well, this name is more bizarre.In fact, he is an application context implementation class that can only read an XML file in the class path as a configuration file.Let me give you another example: FileSystemXmlApplicationContext, what is he doing?Well, he is a filesystem application context, which means he can read the XML anywhere on the disk (read privileges are required) as a configuration file.

So we can instantiate our IOC container:

package com.demo;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Test {
    public static void main(String[] args) {
        //The constructor parameter is the profile name
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        Shape circle = (Shape) applicationContext.getBean("circle");
        circle.draw();
    }}

So we get the objects inside the container, is that basically the same as my copy?By reading this, you should have understood the principles of spring IOC. Later, I will update a new article to analyze the details of spring IOC and some other features.Finally, take a screenshot to see if you don't know the structure of the project.


Keywords: Java Spring xml encoding

Added by edkellett on Fri, 08 May 2020 20:56:05 +0300