[灵性编程]GO的依赖注入 AND 自动生成代码

依赖#

总结下先有的获取对象依赖方式

  1. 比较原始的 New, 全局 global 保存
  2. 基于反射读取对象的依赖,程序启动时由 DI 库实例化 (代表作 dig 等)
  3. 基于反射读取对象的依赖,编译前生成完整构建函数 (代表作 wire 等)

第一种:最方便,直接快捷,大量依赖时候,但是因为是手动的,容易出现实例顺序非预期,不方便自动测试,mock 等。

第二种:因为是启动时反射获取依赖的,需要定义额外的函数给 DI 系统解析,例如一个结构的注入必须要要额外的代码,非常麻烦,不建议使用

// 提供者
err := c.Provide(func(conn *sql.DB) (*UserGateway, *CommentGateway, error) {
  // ...
})
if err != nil {
  // ...
}
// 使用者
err := c.Invoke(func(l *log.Logger) {
  // ...
})
if err != nil {
  // ...
}

第三种,同样是基于反射,所以依然需要一个额外函数 (只有配置信息) 提供反射信息,生成同名函数,便捷度基本和手动 New 一致,wire 由 Google 开源

func InitializeNewGormProvider() *Gorm {
   wire.Build(NewGormProvider, InitializeNewConfProvider)

   return nil
}

我的方案#

原理和 wire 一样,根据配置信息生成自动构建函数,但是不基于反射,因为反射需要程序是完整的,编译后才读取信息,相对慢,需要每个目录改完手动执行 wire . 命令 (每个目录每次花费 1 秒等)。

先看一个场景,数据库服务是依赖配置服务,从结构体就能看出来,不需要
func InitializeNewGormProvider() *Gorm{} 函数反射,未了更加准确 (防止注入了不需要的内容) 添加一个 taginject:""@Bean 注解

// @Bean
type Gorm struct {
    conf      *Conf `inject:""`
}

所以,注入其实是可以直接基于源码的信息都能实现的。

我只要实现一个 go 代码解析工具,就能生成和 wire 工具生成相同的代码,因为 go 源码的关键字和结构实在是太简单了,没有多少语法糖,做一下分词再按语法规则读取源码信息,工具实现比较容易

工具使用 php 实现 (公司都是 mac,php 环境 mac 电脑自带,方便使用模版生成 go 代码)
github.com/go-home-admin/home-tool...
重要是 php 解析很快,整个项目生成一次都是一秒内

ORM 生成代码#

编写工具后,也可以生成其他辅助代码,例如原始结构,添加 @Orm 后,自动根据字段信息生成通用代码

// @Orm
type Gorm struct {
    Id       uint32 `json:"id"`
    UserName string `json:"user_name"`
}

逻辑就可以直接使用

    u := &UsersTable{}
    data := u.WhereUserName("test").And(func(table *UsersTable) {
        table.WhereId(1).OrWhereId(2)
    }).Or(func(table *UsersTable) {
        table.WhereId(2).Or(func(table *UsersTable) {
            table.WhereId(1)
        })
    }).Find()

    // select * form users where user_name = ? and (id = ? or id = ?) or (id = ? or (id = ?))
    utils.Dump(data)
本作品采用《CC 协议》,转载必须注明作者和本文链接