DI (Dependecy Injection)

A Java object depends on other objects. A dependency, such as a Service that relies on a DAO, only declares the object to be null and does not assign the value to it

Inversion of Control

There is no need to control all dependencies and assembly, there are containers for automatic assembly

The following example, a simple implementation of an IoC container, helps understand dependency injection and inversion of control

(in this demo, it is built based on maven, using springframework Autowired, but it is not important, you can generate an annotation to replace it.)

Briefly describe the implementation process

  1. inresourceCreate a new one in the directorypropertiesA configuration file to configure the bean service to be autowled and the corresponding qualified full class name, which might look like this:

2. Use corresponding annotations on the objects that need auto-assembly, such as inUserServiceClassUserDaoObject, add one@AutowiredNotes (isn’t itAutowiredNot important, notes can be replaced)

public class UserService {
    @Autowired 
    private UserDao userDao;

    public User getCurrentLoginUser(a) {
        return userDao.getUserById(1); }}Copy the code
  1. (The following code is posted at the end.) When starting the container, useNew Properties("[Your profile path]")Read the contents of the configuration file and get the configured service name and its qualified full class name
  2. Declare aMapUsed to store object instances to be generated by reflection, traversalpropertiesObject, throughClass.forName(" all-class name ").getconstructor ().newinstance ()Gets the object instance and passesMap.put (Service name, object instance)storage
  3. traverseMapGets the object generated by reflectionclassObject that is calledgetDeclaredFields()Method to get all fields, filter out the field contains@AutowiredAnnotate the field and return oneFieldAn array of objects
  4. Iterate through the array of Field objects, get the name of the Field, and selectprivateTo set the property to an accessible statefield.setAccessible(true)After the callfield.set()Pass in the object instance corresponding to the field and the object to be assigned, and the object to be assigned fromMap.get(fieldName)To obtain
  5. This is the end of dependency injection
  6. You can expose an Api calledgetBeanThe method is passed inbeanReturn the name of theMap.get([name])In order to getBeanThe instance

(The attribute name must be the same as that in the configuration file. Otherwise, the corresponding instance in the Map cannot be found and the attribute will be null.)

The core code

The UserService relies on the UserDao, identified with annotations

public class UserService {
    @Autowired
    private UserDao userDao;

    public User getCurrentLoginUser(a) {
        // getUserById returns the simulated user object
        return userDao.getUserById(1); }}Copy the code

UserDao

public class UserDao {
    public User getUserById(Integer id) {
        System.out.println("A user is returned.");
        return new User(id, "user"+ id); }}Copy the code

The container code

import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;

public class MyIoCContainer {
    private Map<String, Object> beansCache = new HashMap<>();

    public static void main(String[] args) {
        // Create a container and start it
        MyIoCContainer container = new MyIoCContainer();
        container.start();
        // Get the instance through getBean and call its methods
        UserService userService = (UserService) container.getBean("userService");
        userService.getCurrentLoginUser();
    }

    // Start the container
    public void start(a) {
        Properties properties = new Properties();
        try {
            properties.load(MyIoCContainer.class.getResourceAsStream("/beans.properties"));
            properties.forEach((propertyName, propertyClassName) -> {
                try {
                    beansCache.put((String) propertyName, 
                            Class.forName((String) propertyClassName).getConstructor().newInstance());
                } catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) { e.printStackTrace(); }}); }catch (IOException e) {
            e.printStackTrace();
        }
        // Use lambdab expressions to simplify code
        beansCache.forEach((beanName, beanInstance) -> dependencyInjection(beanInstance, beansCache));
    }

    // Implement dependency injection
    private void dependencyInjection(Object beanInstance, Map<String, Object> beansCache) {
        // Stream combines lambda expressions
        // Get all fields annotated with @autowiredList<Field> fieldList = Arrays.stream(beanInstance.getClass().getDeclaredFields()) .filter(field -> field.getAnnotation(Autowired.class) ! =null)
            .collect(Collectors.toList());
        
        // Automatically assemble corresponding object instances
        fieldList.forEach(field -> {
            String fieldName = field.getName();
            field.setAccessible(true);
            try {
                field.set(beanInstance, beansCache.get(fieldName));
            } catch(IllegalAccessException e) { e.printStackTrace(); }}); }// Get a bean from the container
    public Object getBean(String beanName) {
        returnbeansCache.get(beanName); }}Copy the code
In the UserService, only the userDao attribute is declared and no value is assigned. Through the interruption point, you can observe that the userDao object is successfully injected. If the userDao is injected with annotations in more than one place, you can observe that the same object instance is obtained during debugging

The userDao method also executes normally, printing out and returning a mock User object

The core implementation of Spring is much more complex than this, but is essentially a configuration-reflection-auto-assemble process handler. Instead of writing out a Spring framework by hand, you write out a similar pattern to further your understanding of Spring.