golang-open-source-projects icon indicating copy to clipboard operation
golang-open-source-projects copied to clipboard

【开源自荐】 Gozz 强大的注解分析及代码生成工具 提供 依赖注入 AOP API代码生成等

Open Just-maple opened this issue 1 year ago • 3 comments

github | 中文文档

Gozz 是一个十分强大的注解代码生成工具

这是笔者在以往工作中在多个团队成功落地,已经使用了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")
}

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

```go
// 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 还提供了运行时库的工具 ,可以协助我们对对象进行运行时分析,并输出架构图

svg

Just-maple avatar Oct 26 '23 13:10 Just-maple