Skip to main content

什么是IOC?什么是DI?

作者:程序员马丁

在线博客:https://open8gu.com

note

大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。

回答话术

1. 基本概念

IOC 即控制反转(Inversion of Control),它是一种设计原则,它强调将对象之间的依赖关系的控制权交给容器来管理。DI 即依赖注入(Dependency Injection),它是 IOC 的一种实现方式

实际上,IOC 比较常见的实现方式有两种:

  • 依赖查找:即根据某种标识符从特定的容器或上下文获取依赖,比如 Spring 通过 beanName 从容器中获取组件。除 Spring 外,不少的开源框架通常会有一个用于存放各种组件的全局配置类或上下文,等运行时再从中获取相关组件(比如 Mybatis),这种也可以理解为一种依赖查找。
  • 依赖注入:即根据配置,由框架在特定的时机主动的将依赖注入通过构造器或者方法注入到对象里面,我们熟悉的 Spring 就是用来干这个的。

Spring 是 Java 中最主流的 IOC 框架,它既支持依赖查找,又支持依赖注入。除 Spring 之外,我们也可以选择使用谷歌的 Guice,它是一款更轻量的 DI 框架,它通常和不依赖 Spring 生态的其他框架一起使用(比如 Vert.x)。

2. 为什么需要 DI?

下面是一个非常简单的依赖注入的例子:

@Service
public class UserService {
@Autowried
private Database database;
}

在没有 Spring 之前,我们会这样写:

UserService service = new UserService();
Database database = new Database();
service.setDatabase(database);

实际上,光看这个例子,我们可能很难理解 DI 的必要性,因为在这个例子中,我们极度简化了组件间的依赖关系,而实际中,组件间的依赖关系往往会及其复杂,手动维护非常困难

比如,我们假设 UserService 需要一个基于 MyabtisPlus 的 Mapper 代理类 UserMapper,那么我们需要:

// 1、创建 MybatisConfiguration
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.addMapper(UserMapper.class);
configuration.setLogImpl(Slf4jImpl.class);

// 2、创建 GlobalConfig 和 DefaultSqlInjector
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setSqlInjector(new DefaultSqlInjector());
globalConfig.setSuperMapperClass(BaseMapper.class);
GlobalConfigUtils.setGlobalConfig(configuration, globalConfig);

// 3、创建 DataSource
Map<String, String> properties = new HashMap<>();
properties.put("url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=MySQL;INIT=SET NAMES 'UTF-8'");
properties.put("username", "username");
properties.put("password", "password");
properties.put("driverClassName", "org.h2.Driver");
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

// 4、创建 Environment 和 JdbcTransactionFactory
Environment env = new Environment("test", new JdbcTransactionFactory(), dataSource);
configuration.setEnvironment(env);

// 5、创建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

// 6、创建 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

// 7、创建 UserMapper
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

表面上我们只需要一个 UserMapper 代理类,实际上为了创建代理类,我们先需要创建出 9 个彼此依赖的前置组件。

实际上,这仍然是简化后的代码,在项目中,GlobalConfig 这类的配置对象还需要在其他的地方复用、数据源还需要基于数据库连接池进行配置,并且事务也需要与 Spring 或其他框架进行集成……这又涉及到了一大堆彼此依赖的组件。

这甚至还只是 ORM 框架的部分,在项目中,我们还需要引入其他框架或组件,比如限流,注册中心……这种情况下,完全靠手动管理各种对象,就显得不太现实。