In the Spring IOC

IoC is an Inversion of Control feature. It’s not a Spring feature or a Java feature. It’s a design idea. DI(Dependency Injection) is an implementation of Ioc. As for the specific definition of Ioc and DI, as well as their advantages and disadvantages, you can find some information by yourself. I won’t go into details here. In a word, spring’s Ioc function greatly facilitates our development work.

Before implementing our Ioc, let’s take a look at dependency injection in Spring. There are three methods of dependency injection in Spring:

  1. Interface Injection
  2. Set method Injection
  3. Constructor Injection
@Component
public class ComponentA {
    @Autowired // 1. Interface injection
    private ComponentB componentB;
    
    @Autowired // 2. Set method injection
    public void setComponentB(ComponentB componentB) {
        this.componentB = componentB;
    }

    @Autowired // 3. Construct injection
    public ComponentA(ComponentB componentB) {
        this.componentB = componentB; }}Copy the code

Cyclic dependency injection

If you just implement dependency injection, it’s actually quite easy to ‘inject’ the corresponding properties using Java’s reflection principle. But there is one problem that you must be aware of, and that is the problem of circular dependencies. Cyclic dependency is A cycle formed by the interdependence of classes. For example, A depends on B, and B depends on A at the same time, which forms A mutual loop.

// ComponentA
@Component
public class ComponentA {
    @Autowired
    private ComponentB componentB;
}

// ComponentB
@Component
public class ComponentB {
    @Autowired
    private ComponentA componentA;
}
Copy the code

So how do we solve the problem of loop dependency in Spring? Let’s talk about the principle.

If you want to create a class, first put the class in ‘creating pool’, create an instance by reflection, etc., then put the instance in the create pool and remove the class in ‘creating pool’. Whenever an instance has a dependency that needs to be injected, the dependency is created first by looking for the corresponding instance from the creation pool.

Using the in-process state cache, the Bean can be created by putting the Bean in the in-process state even if the dependency is not instantiated, and then running to create the dependency. If the dependency class is dependent on the Bean, just pull the Bean out in the in-process pool. Injection into this dependency ensures that the Bean’s dependency can be instantiated. Go back and inject the dependency into the Bean, then the Bean is instantiated, move the Bean from ‘creating pool’ to ‘creating pool’, and the circular dependency problem is resolved.

Although Spring does an excellent job of avoiding circular dependencies, there is virtually no way that construction injection can avoid circular dependencies. Because when instantiating the constructor of ComponentA, we must get the instance of ComponentB, but when instantiating the constructor of ComponentB, we must have the instance of ComponentA. Both Bean can’t through reflection instantiate and then into the ‘is to create a pool, so I can’t solve the problem of circular dependencies, then spring will take the initiative to throw BeanCurrentlyInCreationException exception to avoid infinite loop.

* Note that the above mentioned are based on spring singleton mode, if it is a multi-instance mode will be different, you can learn by yourself.

To realize the IOC

Now you can start implementing IOC functionality

Add annotations

Create an Annotation package under the ZBW. Ioc package, and then create an Autowired annotation. The annotation Target has only one elementType. FIELD, which can only be annotated on attributes. This means that we currently only implement interface injection. This avoids the problem of circular dependencies caused by construct injection, and interface injection is the most commonly used method. If you want to implement set value injection you can do it yourself, it’s pretty much the same.

package com.zbw.ioc.annotation;

import.@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}

Copy the code

Implementation IOC class

package com.zbw.ioc;

import.@Slf4j
public class Ioc {

    /** * Bean container */
    private BeanContainer beanContainer;

    public Ioc(a) {
        beanContainer = BeanContainer.getInstance();
    }

    /** * execute Ioc */
    public void doIoc(a) {
        for(Class<? > clz : beanContainer.getClasses()) {// Iterate over all beans in the Bean container
            final Object targetBean = beanContainer.getBean(clz);
            Field[] fields = clz.getDeclaredFields();
            for (Field field : fields) { // Iterate over all properties in the Bean
                if (field.isAnnotationPresent(Autowired.class)) {// If the property is annotated by Autowired, inject it
                    finalClass<? > fieldClass = field.getType(); Object fieldValue = getClassInstance(fieldClass);if (null! = fieldValue) { ClassUtil.setField(field, targetBean, fieldValue); }else {
                        throw new RuntimeException("Cannot inject corresponding class, target type :" + fieldClass.getName());
                    }
                }
            }
        }
    }

    /** * get an example or implementation Class */ based on Class
    private Object getClassInstance(finalClass<? > clz) {
        returnOptional .ofNullable(beanContainer.getBean(clz)) .orElseGet(() -> { Class<? > implementClass = getImplementClass(clz);if (null! = implementClass) {return beanContainer.getBean(implementClass);
                    }
                    return null;
                });
    }

    /** * get the implementation class of the interface */
    privateClass<? > getImplementClass(finalClass<? > interfaceClass) {return beanContainer.getClassesBySuper(interfaceClass)
                .stream()
                .findFirst()
                .orElse(null); }}Copy the code

After knowing the principle of IOC, I found that it is really very simple. Here, I realized the function of IOC with dozens of lines of code.

Firstly, the BeanContainer container that we have singleton before is obtained in the Ioc class construction.

Then it is in the doIoc() method that IOC functionality is formally implemented.

  • Iterate over all beans in the BeanContainer
  • The Field property of each Bean is traversed
  • If a Field property isAutowiredAnnotation, then callsgetClassInstance()Method to inject it
  • getClassInstance()We will try to get the corresponding instance from the Bean container according to the Class of the Field. If we get the instance, we will return the instance. If we do not get the Field, we will consider it as an interface and we will callgetImplementClass()Method to obtain the implementation Class of the interface, and then obtain the corresponding implementation Class instance in the Bean container based on the implementation Class.

The test case

To test that our Ioc and BeanContainer are written correctly, let’s write a test case to test it.

Start by adding junit’s dependencies to POM.xml

<properties>.<junit.version>4.12</junit.version>
</properties>
<dependencies>.<! -- junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>
Copy the code

Then add DoodleController, DoodleService and DoodleServiceImpl to test package for convenience

// DoodleController
package com.zbw.bean;
@Controller
@Slf4j
public class DoodleController {
    @Autowired
    private DoodleService doodleService;

    public void hello(a) { log.info(doodleService.helloWord()); }}// DoodleService
package com.zbw.bean;
public interface DoodleService {
    String helloWord(a);
}

// DoodleServiceImpl
package com.zbw.bean;
@Service
public class DoodleServiceImpl implements DoodleService{
    @Override
    public String helloWord(a) {
        return "hello word"; }}Copy the code

Then write the IocTest test case

package com.zbw.ioc;

import.@Slf4j
public class IocTest {
    @Test
    public void doIoc(a) {
        BeanContainer beanContainer = BeanContainer.getInstance();
        beanContainer.loadBeans("com.zbw");
        newIoc().doIoc(); DoodleController controller = (DoodleController) beanContainer.getBean(DoodleController.class); controller.hello(); }}Copy the code

See the string in the helloWord() method that outputs the DoodleServiceImpl in the DoodleController, indicating that the DoodleService in the DoodleController has successfully injected the DoodleServiceImpl. Then our IOC function is complete.


  • Implement a simple Java MVC framework from scratch (I)– Preface
  • Implement a simple Java MVC framework from scratch (two)- implement Bean container
  • Implement a simple Java MVC framework from scratch (iii)– implement IOC
  • Implement a simple Java MVC framework from scratch (four)– implement AOP
  • Implementing a simple Java MVC framework from scratch (5)– Introducing AspectJ to implement AOP pointcuts
  • Implement a simple Java MVC framework from scratch (6)– enhance AOP functionality
  • Implement a simple Java MVC framework from scratch (7)– Implement MVC
  • Implement a simple Java MVC framework from scratch (8)- Starter
  • Implement a simple Java MVC framework from scratch (9)– optimize THE MVC code

Source address :doodle

Implement a simple Java MVC framework from scratch — implement IOC