IoC全称Inversion of Control,直译为控制反转.
首先是一个常见的场景,比如我们一共有三个类: BookService, UserService, HistoryService
这三个类都需要执行以下操作

  1. 初始化数据库连接
  2. 查询数据
  3. 返回数据

这就带来了问题,

  1. 实例化一个类的时候会带来冗余的代码,比如初始化数据库连接 。
  2. 组件里面依赖的资源需要进行销毁? 但是这个组件可能又被其他组件依赖,这样依赖很容易出现资源无法销毁的情况
  3. 一个组件里面,实例化太多的组件,依赖关系复杂,不好测试,容易出错

解决方法:
把这些组件的创建,交给系统管理,而不是在类里面实例化组件,而是统一由系统创建,最后由系统销毁

如何做?

  1. Spring里面的 IOC 负责创建组件
  2. IOC 负责根据依赖关系组装组件
  3. IOC 负责销毁和关闭资源

一般有2种方法,一个是通过 set* 方法注入,一个是通过构造函数注入. 这里都做一下记录

set 和 构造函数注入

首先设置三个类: UserService / EmailService / LogService

UserService

  1. package com.example.code.services;
  2. public class UserService {
  3. private EmailService emailService;
  4. private LogService logService;
  5. // 构造函数注入
  6. public UserService(LogService logService){
  7. this.logService = logService;
  8. }
  9. // 通过 set* 方法注入
  10. public void setEmailService(EmailService emailService) {
  11. this.emailService = emailService;
  12. }
  13. public User Register(){
  14. System.out.println("Register User");
  15. emailService.SendEmail(); // send email...
  16. System.out.println("register successful");
  17. this.logService.record("UserService"); // log information for user service
  18. return new User();
  19. }
  20. }

新建 Main.java 启动函数

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = context.getBean(UserService.class);
        User user = userService.Register();
    }
}

管理注入的XML为

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="logService" class="com.example.code.services.LogService" />

    <bean id="userService" class="com.example.code.services.UserService">
        <!-- set 函数注入 -->
        <property name="emailService" ref="emailService" />
        <!-- 构造函数注入 -->
        <constructor-arg name="logService" ref="logService" />
    </bean>

    <bean id="emailService" class="com.example.code.services.EmailService">
        <constructor-arg name="logService" ref="logService" />
    </bean>

</beans>

工厂函数静态注入

最后还有一种工厂函数注入
我们新建一个类 CheckService.java

/*
 * @Description:
 * @Date: 2020/10/13
 */
package com.example.code.service;

public class CheckService {
    public void check(){
        System.out.println("checking...");
    }
}

然后创建 工厂方法, CheckServiceFactory.java


public class CheckServiceFactory {
    public static final CheckService createCheckService(){
        return new CheckService();
    }
}

然后注入到 UserService 中

public class UserService {
    private CheckService checkService;

    public void setCheckService(CheckService checkService) {
        this.checkService = checkService;
    }

    public String register(){
        checkService.check();
        emailService.sendEmail();
        return "register successful";
    }
}

跟 set 方法注入没什么不同,但是 xml 的配置是不同的

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="checkService" class="com.example.code.service.CheckServiceFactory" factory-method="createCheckService"/>
    <bean id="userService" class="com.example.code.service.UserService">
        <property name="checkService" ref="checkService" />
    </bean>

</beans>

这里指向了 工厂类,还指明了工厂函数。 spring 在读取配置的时候,到这个地方,就不会去调用类的反射,而是交由工厂函数去处理实例化组件。

工厂函数实例注入

最后一种是实例工厂的方法注入
新建 LogService.java

/*
 * @Description:
 * @Date: 2020/10/13
 */
package com.example.code.service;

public class LogService {
    public void record(String name){
        System.out.println("recode log: " + name);
    }
}

然后常见工厂方法

/*
 * @Description:
 * @Date: 2020/10/13
 */
package com.example.code.service;

public class LogServiceFactory {
    public LogService createLogService(){
        return new LogService();
    }
}

然后是UserService.java

public class UserService {    
    private LogService logService;

    public void setLogService(LogService logService) {
        this.logService = logService;
    }

    public String register(){
        logService.record("user");
        return "register successful";
    }
}

最后看一下 xml

<bean id="logService" factory-bean="logServiceFactory" factory-method="createLogService" />
<bean id="logServiceFactory" class="com.example.code.service.LogServiceFactory" />
<bean id="userService" class="com.example.code.service.UserService">
  <property name="logService" ref="logService" />
</bean>