Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Just-maple opened this issue Oct 26, 2023 · 3 comments

Comments

@Just-maple
Copy link

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

@dolphin0520
Copy link

dolphin0520 commented Oct 26, 2023 via email

@iamcaomin
Copy link

iamcaomin commented Oct 26, 2023 via email

@Kaimar666
Copy link

Kaimar666 commented Oct 26, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants