The generalized IOC

  • IoC — “Don’t call me, we’ll call you.”

There are two implementations: dependency lookup (DL) and dependency injection (DI).

The relationship between IOC and DI and DL (the DL, Avalon, and EJB implementations are IOC in this way) :

  • The DL has been abandoned because it requires users to find resources and assemble objects using the API themselves. It’s invasive.
  • DI is the approach used by Spring, where the container is responsible for assembly of components.

Note: It’s not just Spring that uses DI for Java to implement IoC, including Google’s Guice, but also the less popular PicoContainer (extremely lightweight, but only IoC).

Spring 的 IoC

Spring’s IoC design supports the following features:

  • Dependency injection
  • Depend on the check
  • Automatically.
  • Support the collection
  • Specify initialization and destruction methods
  • Support for callbacks to some methods (but need to implement the Spring interface, slightly intrusive)

The most important of these is dependency injection, which, in XML configuration terms, is the REF tag. Corresponds to the Spring RuntimeBeanReference object.

For the IoC, the most important thing is the container. The container manages the Bean life cycle and controls the dependency injection of beans.

So how does Spring design containers?

Spring author Rod Johnson designed two interfaces to represent containers.

  • BeanFactory
  • ApplicationContext

A BeanFactory is crude and simple, and can be understood as a HashMap, where the Key is a BeanName and the Value is a Bean instance. Usually, only register (PUT) and get (get) functions are provided. We can call this a “low-level container.”

ApplicationContext can be called a high-level container. Because it has more features than BeanFactory. He inherits multiple interfaces. So it has more functions. For example, resource fetching, support for multiple messages (such as JSP tag support), and tool-level support for BeanFactory waiting. So its name is no longer a BeanFactory or something, but an “application context”, which stands for all the functionality of a large container. This interface defines a refresh method, familiar to anyone reading Spring source code, for refreshing the entire container, that is, reloading/refreshing all beans.

Of course, there are other auxiliary interfaces besides these two big ones, but I won’t spend too much time on them today.

In order to more intuitive display “low-level container” and “advanced container” relationship, I’m here by the commonly used ClassPathXmlApplicationContext class, to show the hierarchy of the whole container UML.

A little complicated? Don’t panic. Let me explain.

You know the BeanFactory at the top? I won’t go into it.

The following three green ones are function extension interfaces, which will not be expanded here.

Look below at the “high-level container” of the subordinate ApplicationContext in pink. It depends on the “low-level container”. This means dependency, not inheritance. It relies on the “low-level container” getBean functionality. High-level containers have more capabilities: support for different information sources, access to file resources, and support for application events (Observer mode).

Typically what users see is “high-level containers.” But the BeanFactory is also very useful!

The gray area on the left is the “low-level container” that loads only beans and retrieves them. Other advanced features of the container are not available. For example, refresh the Bean factory configuration shown above. Lifecycle event callbacks, etc.

Ok, with low-level and high-level containers explained, we can take a look at what an IoC startup process looks like. To put it bluntly, is the class ClassPathXmlApplicationContext, at startup, have done nothing. (Since this is interface21 code, it must be different from your Spring 4.x series).

Below is the structure of the ClassPathXmlApplicationContext process, the actual is Spring IoC initialization process.

Notice that this is a simplification for the sake of understanding.

Here are some words to describe the process:

  1. Users construct ClassPathXmlApplicationContext (hereinafter referred to as the CPAC)

  2. CPAC first accesses the final Refresh method of the “Abstract high-level container,” which is the template method. So call back to the refreshBeanFactory method of the tone class, which loads all beandefinitions and Properties into the container using the low-level container.

  3. After the low-level container loads successfully, the high-level container starts to handle some callbacks, such as Bean post-handlers. Call the setBeanFactory method. Or register listeners, publish events, instantiate singleton beans, etc., these functions, with the continuous upgrade of Spring, more and more functions, many people lost their way here :).

To put it simply:

  1. The low-level container loads the configuration files (from XML, databases, applets) and parses them into BeanDefinitions into the low-level container.
  2. Once loaded, the high-level container starts advanced functions, such as interface callbacks, listeners, automatic instantiation singletons, publishing events, and so on.

So, be sure to make a clear distinction between “low-level” and “high-level” containers. You can’t see the wood for the trees.

Well, once we have created the container, we will use the getBean method to get the Bean, and the getBean process is as follows:

As you can see from the figure, getBean operations are performed in low-level containers. There’s a recursive operation. What does that mean?

Assumptions: When Bean_A depends on Bean_B, and Bean_A is loaded, its configured ref = “Bean_B” is parsed as a placeholder into Bean_A’s properties set. When getBean is called, To inject real Bean_B into Bean_A, you need to get that Bean_B from the container, thus generating recursion.

Why not inject it directly at load time? Because the loading order is different, it is likely that Bean_B, which Bean_A depends on, has not been loaded properly and cannot be fetched from the container. You cannot ask the user to order the beans in the loading order, which is inhuman.

So, Spring breaks it down into two steps:

  1. Load all beans configured as BeanDefinitions into the container, temporarily replacing them with placeholders if the Bean has dependencies.
  2. Then, when you call getBean, you do real dependency injection, that is, if you encounter a (placeholder) property of REF, you get the Bean from the container and inject it into the instance — called dependency injection.

As you can see, dependency injection is in fact only a “low-level container”.

That’s IoC.

Therefore, the operation in the ApplicationContext Refresh method is not only IoC, but all the functionality of the high-level container (including IoC), which can be implemented in the low-level container.

conclusion

Having said so much, I wonder if you understand Spring IoC? To summarize, IoC is implemented in Spring using only low-level containers. There are two steps:

A. Load the configuration file, parse it into BeanDefinition, and place it in the Map.

B. When calling getBean, instantiate the Class object from the BeanDefinition Map and recursively call the getBean method if there are dependencies.

This is the IoC of the Spring low-level container (BeanFactory).

As for the high-level container ApplicationContext, it contains the functionality of the low-level container, which refreshes the beans of the entire container when it executes the Refresh template method. Also, as a high-level container, it contains too much functionality. In short, he is not just the IoC. It supports different information sources, BeanFactory utility classes, hierarchical containers, access to file resources, event notification, interface callbacks, and more.

As Spring continues to evolve, you can expect to see more and more functionality for high-level containers.

To be sure, understanding the IoC process is really about understanding the timing of callbacks to various interfaces when Spring is initialized. For example, InitializingBean, BeanFactoryAware, ApplicationListener, etc. The functions of these interfaces are described in this article.

Note, however, that implementing the Spring interface means your application is bound to Spring! Means Spring is intrusive! Remember, when Spring was released, one of his biggest claims was that it was non-invasive — that the IoC container could be changed without changing the code. Today, Spring is a quasi-official solution in the J2EE community, and there is no such thing as intrusive. Because it’s the standard, like servlets, can you not implement the interface of servlets? : -)

Well, the next time an interviewer asks about the Spring IoC initialization process, there will be no more equivocations!!