21 April 2019
Spring(1)----IoC
by Jerry Zhang
#Tip of the Day: Long way to go
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
- Package: org.springframework.beans, org.springframework.context
- Interface: BeanFactory: provides the configuration framework and basic functionality. ApplicationContext: adds more enterprise-specific functionality.