头图

Gozz 是一个十分强大的注解分析及模板化代码生成工具

依赖注入和AOP是其内置插件中提供的强大功能之一。

这是笔者在以往工作中在多个团队成功落地,已经使用了4年以上的成熟方案,最近终于有时间进行开源。

这里搬运一个 中文文档 提供的示例二 比如下面是一个很常见的应用 基本层级代码

package overview02

import (
    "context"
    "database/sql"
    "fmt"
    "net/http"
    "time"

    "github.com/go-redis/redis/v8"
)

type (
    // root config for unmarshal config file
    Config struct {
        Server ServerConfig `yaml:"server"`
        Sql    SqlConfig    `yaml:"sql"`
        Redis  RedisConfig  `yaml:"redis"`
    }

    // http server config
    ServerConfig struct {
        Addr string `yaml:"addr"`
    }

    // sql config
    SqlConfig struct {
        Dsn string `yaml:"dsn"`
    }

    // redis config
    RedisConfig struct {
        Host string `yaml:"host"`
        Port string `yaml:"port"`
    }
)

// provide http server from server config
func ProvideHttpServer(config ServerConfig) *http.Server {
    return &http.Server{
        Addr: config.Addr,
    }
}

// interface of sql connection
type SqlConn interface {
    QueryContext(ctx context.Context, statement string, args ...interface{}) (rows *sql.Rows, err error)
}

// interface of key value store
type Store interface {
    Get(ctx context.Context, key string) (value []byte, err error)
    Set(ctx context.Context, key string, value []byte, exp time.Duration) (err error)
}

// provide sql connection from sql config
func ProvideSql(config SqlConfig) (*sql.DB, error) {
    return sql.Open("mysql", config.Dsn)
}

// provide kv store from redis config
func ProvideRedisStore(config RedisConfig) (*redis.Client, error) {
    rdb := redis.NewClient(&redis.Options{
        Addr: fmt.Sprintf("%s:%s", config.Host, config.Port),
    })
    return rdb, nil
}

type RedisStore struct {
    redis.Cmdable
}

func (s RedisStore) Set(ctx context.Context, key string, value []byte, exp time.Duration) (err error) {
    return s.Cmdable.Set(ctx, key, value, exp).Err()
}

func (s RedisStore) Get(ctx context.Context, key string) (value []byte, err error) {
    return s.Cmdable.Get(ctx, key).Bytes()
}

// biz service handler
type ServiceHandler interface {
    GetInt(ctx context.Context) (int, error)
    GetString(ctx context.Context) (string, error)
}

// implement of server handler
type ServerHandlerImpl struct {
    Sql   SqlConn
    Store Store
}

func (impl *ServerHandlerImpl) GetInt(ctx context.Context) (int, error) {
    panic("not implemented")
}

func (impl *ServerHandlerImpl) GetString(ctx context.Context) (string, error) {
    panic("not implemented")
}

// the entry of application
type Application interface {
    Run()
}

// web application implement
type application struct {
    Server  *http.Server
    Handler ServiceHandler
}

func (application application) Run() {
    panic("not implemented")
}

我们只需要在对象的注释中加上一些注解 然后运行 gozz 工具 注意 +zz:wire 这样的声明

package overview02

import (
    "context"
    "database/sql"
    "fmt"
    "net/http"
    "time"

    "github.com/go-redis/redis/v8"
)

//go:generate gozz run -p "wire" ./

type (
    // root config for unmarshal config file
    // +zz:wire:field=*
    Config struct {
        Server ServerConfig `yaml:"server"`
        Sql    SqlConfig    `yaml:"sql"`
        Redis  RedisConfig  `yaml:"redis"`
    }

    // http server config
    ServerConfig struct {
        Addr string `yaml:"addr"`
    }

    // sql config
    SqlConfig struct {
        Dsn string `yaml:"dsn"`
    }

    // redis config
    RedisConfig struct {
        Host string `yaml:"host"`
        Port string `yaml:"port"`
    }
)

// provide http server from server config
// +zz:wire
func ProvideHttpServer(config ServerConfig) *http.Server {
    return &http.Server{
        Addr: config.Addr,
    }
}

// interface of sql connection
type SqlConn interface {
    QueryContext(ctx context.Context, statement string, args ...interface{}) (rows *sql.Rows, err error)
}

// interface of key value store
type Store interface {
    Get(ctx context.Context, key string) (value []byte, err error)
    Set(ctx context.Context, key string, value []byte, exp time.Duration) (err error)
}

// provide sql connection from sql config
// +zz:wire:bind=SqlConn
func ProvideSql(config SqlConfig) (*sql.DB, error) {
    return sql.Open("mysql", config.Dsn)
}

// provide kv store from redis config
// +zz:wire:bind=redis.Cmdable
func ProvideRedisStore(config RedisConfig) (*redis.Client, error) {
    rdb := redis.NewClient(&redis.Options{
        Addr: fmt.Sprintf("%s:%s", config.Host, config.Port),
    })
    return rdb, nil
}

// +zz:wire:bind=Store
type RedisStore struct {
    redis.Cmdable
}

func (s RedisStore) Set(ctx context.Context, key string, value []byte, exp time.Duration) (err error) {
    return s.Cmdable.Set(ctx, key, value, exp).Err()
}

func (s RedisStore) Get(ctx context.Context, key string) (value []byte, err error) {
    return s.Cmdable.Get(ctx, key).Bytes()
}

// biz service handler
type ServiceHandler interface {
    GetInt(ctx context.Context) (int, error)
    GetString(ctx context.Context) (string, error)
}

// implement of server handler
// +zz:wire:bind=ServiceHandler:aop
type ServerHandlerImpl struct {
    Sql   SqlConn
    Store Store
}

func (impl *ServerHandlerImpl) GetInt(ctx context.Context) (int, error) {
    panic("not implemented")
}

func (impl *ServerHandlerImpl) GetString(ctx context.Context) (string, error) {
    panic("not implemented")
}

// the entry of application
// +zz:wire:inject=./:param=*Config
type Application interface {
    Run()
}

// web application implement
// +zz:wire:bind=Application
type application struct {
    Server  *http.Server
    Handler ServiceHandler
}

func (application application) Run() {
    panic("not implemented")
}

你会发现完整的依赖注入代码就被生成好了:

// Code generated by Wire. DO NOT EDIT.

//go:generate go run github.com/google/wire/cmd/wire
//+build !wireinject

package overview02

// Injectors from wire_zinject.go:

// github.com/go-zing/gozz-doc-examples/overview02.Application
func Initialize_Application(config *Config) (Application, func(), error) {
    serverConfig := config.Server
    server := ProvideHttpServer(serverConfig)
    sqlConfig := config.Sql
    db, err := ProvideSql(sqlConfig)
    if err != nil {
        return nil, nil, err
    }
    redisConfig := config.Redis
    client, err := ProvideRedisStore(redisConfig)
    if err != nil {
        return nil, nil, err
    }
    redisStore := &RedisStore{
        Cmdable: client,
    }
    serverHandlerImpl := &ServerHandlerImpl{
        Sql:   db,
        Store: redisStore,
    }
    overview02_impl_aop_ServiceHandler := &_impl_aop_ServiceHandler{
        _aop_ServiceHandler: serverHandlerImpl,
    }
    overview02Application := &application{
        Server:  server,
        Handler: overview02_impl_aop_ServiceHandler,
    }
    return overview02Application, func() {
    }, nil
}

而且还可以生成自动化注入的 AOP 代理:

// Code generated by gozz:wire github.com/go-zing/gozz. DO NOT EDIT.

package overview02

import (
    "context"
)

type _aop_interceptor interface {
    Intercept(v interface{}, name string, params, results []interface{}) (func(), bool)
}

// ServiceHandler
type (
    _aop_ServiceHandler      ServiceHandler
    _impl_aop_ServiceHandler struct{ _aop_ServiceHandler }
)

func (i _impl_aop_ServiceHandler) GetInt(p0 context.Context) (r0 int, r1 error) {
    if t, x := i._aop_ServiceHandler.(_aop_interceptor); x {
        if up, ok := t.Intercept(i._aop_ServiceHandler, "GetInt",
            []interface{}{&p0},
            []interface{}{&r0, &r1},
        ); up != nil {
            defer up()
        } else if !ok {
            return
        }
    }
    return i._aop_ServiceHandler.GetInt(p0)
}

func (i _impl_aop_ServiceHandler) GetString(p0 context.Context) (r0 string, r1 error) {
    if t, x := i._aop_ServiceHandler.(_aop_interceptor); x {
        if up, ok := t.Intercept(i._aop_ServiceHandler, "GetString",
            []interface{}{&p0},
            []interface{}{&r0, &r1},
        ); up != nil {
            defer up()
        } else if !ok {
            return
        }
    }
    return i._aop_ServiceHandler.GetString(p0)
}

Gozz 还提供了运行时库的工具 ,可以协助我们对对象进行运行时分析,并输出架构图

图片

Gozz 还提供了一系列强大的内置插件:

  • Api 基于 interface 快速提供 API接口
  • Doc 基于注释生成运行时文档
  • Impl 同步 interfaceimplement
  • Option 生成 Funcitonal Options 风格代码
  • Orm 生成数据实体映射代码
  • Tag 模版化管理结构体字段标签
  • Wire 自动化依赖注入 及 AOP代理

更多有趣的使用可以访问 Github 探索,也欢迎提 Issue 和 Star


Maple
2 声望0 粉丝