Midway 系列的特色是依赖注入,所谓的依赖注入就是不需要关心对象创建,只需要简单注入即可执行的解耦方式。

    我们举个例子,以下面的函数目录结构为例。

    1. .
    2. ├── f.yml # 标准化 spec 文件
    3. ├── package.json # 项目依赖
    4. ├── src
    5. ├── function # 函数的目录
    6. └── index.ts
    7. └── service # 依赖的服务目录
    8. └── userService.ts
    9. └── tsconfig.json

    在上面的示例中,提供了两个文件, index.tsuserService.ts 。为了解释方便,我们将它合并到了一起,内容大致如下。

    1. import { Provide, Inject } from '@midwayjs/decorator';
    2. // index.ts
    3. @Provide()
    4. export class IndexHandler {
    5. @Inject()
    6. userService: UserService;
    7. async handler() {
    8. const user = await this.userService.getUser();
    9. console.log(user); // world
    10. }
    11. }
    12. // userService.ts
    13. @Provide()
    14. export class UserService {
    15. async getUser() {
    16. return 'world';
    17. }
    18. }

    抛开两个装饰器,你可以看到这是标准的 class 写法,没有其他多余的内容,这也是 Midway 体系的核心能力,依赖注入最迷人的地方。

    @Provide 的作用是告诉 IoC 容器,我需要被容器所加载。 @Inject 装饰器告诉容器,我需要将某个实例注入到属性上。

    通过这两个装饰器的搭配,我们可以方便的在任意类中拿到实例对象,就像上面的 this.userService

    那么,代码为什么能执行呢?

    我们以下面的伪代码举例,在 midway 体系启动阶段,会创建一个 IoC 容器(MidwayContainer),扫描所有用户代码(src)中的文件,将拥有 @Provide 装饰器的 class,绑定到 IoC 容器中。

    1. /***** 下面为 midway-faas 内部代码 *****/
    2. const container = new MidwayContainer();
    3. container.bind(IndexHandler);
    4. container.bind(UserService);

    在请求时,会动态实例化这些 class,并且处理属性的赋值,比如下面的伪代码,很容易理解。

    1. /***** 下面为 IoC 容器伪代码 *****/
    2. const userService = new UserService();
    3. const indexHandler = new IndexHandler();
    4. indexHandler.userService = userService;

    经过这样,我们就能拿到完整的 indexHandler 了,实际的代码会稍微不一样。

    MidwayContainer 有 getAsync 方法,用来异步处理对象的初始化(很多依赖都是有异步初始化的需求),自动属性赋值,缓存,返回对象,将上面的流程合为同一个。

    1. /***** 下面为 IoC 容器内部代码 *****/
    2. // 自动 new UserService();
    3. // 自动 new IndexHandler();
    4. // 自动赋值 indexHandler.userService = await container.getAsync(UserService);
    5. const indexHandler = await container.getAsync(IndexHandler);
    6. await indexHandler.handler(); // output 'world'

    以上就是 IoC 的核心过程,创建实例。

    :::info 此外,这里还有一篇名为 《这一次,教你从零开始写一个 IoC 容器》的文章,欢迎扩展阅读。 :::