Jerry's Blog

Recording what I learned everyday

View on GitHub


21 April 2019

Spring(1)----IoC

by Jerry Zhang

#Tip of the Day: Long way to go



Video Link

What is Spring IoC?

IoC = Inversion of Control.

What to Control? What to Inverse?

Suppose Tom has a car named Audi, and Jerry has a car named Buick. There are four methods in the class Car: start, turnLeft, turnRight, stop. Tom and Jerry want to use their cars to go to work, go home, go shopping, etc. Our code may looks like this:

public class Tom {

    private Car audi = new Car();

    public void goHome() {
        audi.start();
        audi.turnLeft();
        audi.turnRight();
        audi.stop();
    }
}

In this case, Tom depends on a Car. We can say that this Car is a dependency of Tom. Now, think about this: what if Tom wants to change his car to a Buick? And also, should this car be created by Tom?

We have to change all the occurrences if he wanted to change a new car, and obviously, this car should not be made by Tom.

Therefore, the creating, controlling and destroying of a Car should be done by someone else. In Spring, an IoC Container does these dirty works for us. All the objects in a Spring project are called beans, such as Car, Tom, Jerry. They are managed by an IoC container.

A bean can be injected into another bean by its Constructor. Now, our code may look like this:

public class Tom {

    Car car;
    
    public Tom(Car car) {
        super(car);
    }

    public void goHome() {
        car.start();
        car.turnLeft();
        car.turnRight();
        car.stop();
    }
}

Create our own IoC

To understand what an IoC looks like, here is an example of a mock IoC Container. The code contains some Java reflection. You may need to learn Java reflection if you want to totally understand.

public class IoCContainer {

    private Map<String, Object> beans = new ConcurrentHashMap<>();

    public Object getBean(String beanId){
        return beans.get(beanId);
    }

    public void setBeans(Class<?> clazz, String beanId, String... paramBeanId){
        Object[] paramValues = new Object[paramBeanId.length];
        for (int i = 0; i < paramBeanId.length; i++) {
            paramValues[i] = beans.get(paramBeanId[i]);
        }

        Object bean = null;

        for (Constructor<?> constructor : clazz.getConstructors()) {
            try {
                bean = constructor.newInstance(paramValues);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        if (bean == null) {
            throw new RuntimeException("Cannot find proper constructor to create bean");
        }
        beans.put(beanId, bean);

    }
}

The container uses a Map to store all the beans. The key is beanId, the value is the bean object.

Getting a bean is very simple. Get the value by its key.

Setting a bean is a little complex. If we only needed to create a bean with no dependency, such as a Car, we simply tell the container the beanId and the Class type of our bean. What if we need to create a bean like Tom or Jerry, a little more work is needed, because their constructors need some other objects as parameters, such as a Car. Thus, we need to pass in the dependency parameters and tell the container what dependencies are in this bean. To set a bean, the container checks its Map, gets all the beans by the beanIds, then loops the constructor of this bean that we want to create, to find out a proper constructor and create a bean with newInstance(paramValues). Finally, put this bean into the Map.

IoC Container and Beans

tags: Spring - framework - Java - IoC