hero

Gozz

Golang 注解分析及模板化代码工具

快速上手 →

简洁易用无侵入

直观明了的注解语法,干净利落的命令行工具,以及无需添加运行时依赖就能使用的生成代码。

内置强大插件

自动化依赖注入、AOP接口代理、Interface -> Implement 同步、ORM、API路由表等。

高拓展性

可自定义生成模版,提供代码分析、编辑、生成等内核库,基于 .so 快速开发和扩展外部插件。

# 功能快览

# 示例一

这里有一个非常常见的服务接口和结构体定义文件 示例项目 (opens new window)

package overview01

import (
	"context"
	"time"
)

// Book manager service
type BookService interface {
	// Get book entity by id
	Get(ctx context.Context, query QueryId) (book Book, err error)
	// Get book entities list by query params
	List(ctx context.Context, query QueryBook) (list ListBook, err error)
	// Create book entity with form
	Create(ctx context.Context, form FormBook) (book Book, err error)
	// Edit book entity from form
	Edit(ctx context.Context, form FormBook) (book Book, err error)
	// Delete book entity by id
	Delete(ctx context.Context, query QueryId) (err error)
}

type (
	// Uri path id param
	QueryId struct {
		Id int
	}

	// Query book entities param
	QueryBook struct {
		// Query pagination offser no
		PageNo int
		// Query pagination size
		PageSize int
	}

	// Form to edit or create book entity
	FormBook struct {
		QueryId
		// Title of book entity
		Title string
		// Type of book entity
		Type string
	}

	// Book entity
	Book struct {
		FormBook
		// Book entity create time
		CreatedAt time.Time
		// Book entity create user
		CreatedBy string
		// Book entity last update time
		UpdatedAt time.Time
		// Book entity last update user
		UpdatedBy string
	}

	// Book entities list
	ListBook struct {
		// Query pagination total count in database
		Total int
		// Entities list data
		List []Book
	}
)

给它添加上一些注解,大致语法是 +zz:插件名:插件参数










 
 
 



 


 


 


 


 



 
 


 





 






































package overview01

import (
	"context"
	"time"
)

//go:generate gozz run -p "doc" -p "api:prefix=book:foo=bar" -p "impl" -p "tag" ./

// +zz:doc
// +zz:api:./
// +zz:impl:./types.go:type=Implement
// Book manager service
type BookService interface {
	// Get book entity by id
	// +zz:api:get|{id}
	Get(ctx context.Context, query QueryId) (book Book, err error)
	// Get book entities list by query params
	// +zz:api:get
	List(ctx context.Context, query QueryBook) (list ListBook, err error)
	// Create book entity with form
	// +zz:api:post
	Create(ctx context.Context, form FormBook) (book Book, err error)
	// Edit book entity from form
	// +zz:api:put|{id}
	Edit(ctx context.Context, form FormBook) (book Book, err error)
	// Delete book entity by id
	// +zz:api:delete|{id}
	Delete(ctx context.Context, query QueryId) (err error)
}

// +zz:doc
// +zz:tag:json:{{ snake .FieldName }}
type (
	// Uri path id param
	// +zz:tag:uri:{{ snake .FieldName }}
	QueryId struct {
		Id int
	}

	// Query book entities param
	// +zz:tag:query:{{ snake .FieldName }}
	QueryBook struct {
		// Query pagination offser no
		PageNo int
		// Query pagination size
		PageSize int
	}

	// Form to edit or create book entity
	FormBook struct {
		QueryId
		// Title of book entity
		Title string
		// Type of book entity
		Type string
	}

	// Book entity
	Book struct {
		FormBook
		// Book entity create time
		CreatedAt time.Time
		// Book entity create user
		CreatedBy string
		// Book entity last update time
		UpdatedAt time.Time
		// Book entity last update user
		UpdatedBy string
	}

	// Book entities list
	ListBook struct {
		// Query pagination total count in database
		Total int
		// Entities list data
		List []Book
	}
)

运行指令 gozz run -p "doc" -p "api:prefix=book" -p "impl" -p "tag" ./

意思是使用这些插件进行分析并处理

文件被更新为:

package overview01

import (
	"context"
	"time"
)

//go:generate gozz run -p "doc" -p "api:prefix=book" -p "impl" -p "tag" ./

// +zz:doc
// +zz:api:./
// +zz:impl:./types.go:type=Implement
// Book manager service
type BookService interface {
	// Get book entity by id
	// +zz:api:get|{id}
	Get(ctx context.Context, query QueryId) (book Book, err error)
	// Get book entities list by query params
	// +zz:api:get
	List(ctx context.Context, query QueryBook) (list ListBook, err error)
	// Create book entity with form
	// +zz:api:post
	Create(ctx context.Context, form FormBook) (book Book, err error)
	// Edit book entity from form
	// +zz:api:put|{id}
	Edit(ctx context.Context, form FormBook) (book Book, err error)
	// Delete book entity by id
	// +zz:api:delete|{id}
	Delete(ctx context.Context, query QueryId) (err error)
}

// +zz:doc
// +zz:tag:json:{{ snake .FieldName }}
type (
	// Uri path id param
	// +zz:tag:uri:{{ snake .FieldName }}
	QueryId struct {
		// Entity Id
		Id int `json:"id" uri:"id"`
	}

	// Query book entities param
	// +zz:tag:query:{{ snake .FieldName }}
	QueryBook struct {
		// Query pagination offser no
		PageNo int `json:"page_no" query:"page_no"`
		// Query pagination size
		PageSize int `json:"page_size" query:"page_size"`
	}

	// Form to edit or create book entity
	FormBook struct {
		QueryId
		// Title of book entity
		Title string `json:"title"`
		// Type of book entity
		Type string `json:"type"`
	}

	// Book entity
	Book struct {
		FormBook
		// Book entity create time
		CreatedAt time.Time `json:"created_at"`
		// Book entity create user
		CreatedBy string `json:"created_by"`
		// Book entity last update time
		UpdatedAt time.Time `json:"updated_at"`
		// Book entity last update user
		UpdatedBy string `json:"updated_by"`
	}

	// Book entities list
	ListBook struct {
		// Query pagination total count in database
		Total int `json:"total"`
		// Entities list data
		List []Book `json:"list"`
	}
)

var (
	_ BookService = (*Implement)(nil)
)

type Implement struct{}

func (implement Implement) Get(ctx context.Context, query QueryId) (book Book, err error) {
	panic("not implemented")
}

func (implement Implement) List(ctx context.Context, query QueryBook) (list ListBook, err error) {
	panic("not implemented")
}

func (implement Implement) Create(ctx context.Context, form FormBook) (book Book, err error) {
	panic("not implemented")
}

func (implement Implement) Edit(ctx context.Context, form FormBook) (book Book, err error) {
	panic("not implemented")
}

func (implement Implement) Delete(ctx context.Context, query QueryId) (err error) {
	panic("not implemented")
}

目录下生成了可用于快速注册API接口的文件 zzgen.api.go

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

package overview01

import (
	"context"
)

var _ = context.Context(nil)

type Apis struct {
	BookService BookService
}

func (s Apis) Iterate(fn func(interface{}, []map[string]interface{})) {
	for _, f := range []func() (interface{}, []map[string]interface{}){
		s._BookService,
	} {
		fn(f())
	}
}

func (s Apis) _BookService() (interface{}, []map[string]interface{}) {
	t := s.BookService
	return &t, []map[string]interface{}{
		{
			"name":     "Get",
			"resource": "get|{id}",
			"options": map[string]string{
				"prefix": "book",
			},
			"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
				var in QueryId
				if err := dec(&in); err != nil {
					return nil, err
				}
				return t.Get(ctx, in)
			},
		},
		{
			"name":     "List",
			"resource": "get",
			"options": map[string]string{
				"prefix": "book",
			},
			"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
				var in QueryBook
				if err := dec(&in); err != nil {
					return nil, err
				}
				return t.List(ctx, in)
			},
		},
		{
			"name":     "Create",
			"resource": "post",
			"options": map[string]string{
				"prefix": "book",
			},
			"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
				var in FormBook
				if err := dec(&in); err != nil {
					return nil, err
				}
				return t.Create(ctx, in)
			},
		},
		{
			"name":     "Edit",
			"resource": "put|{id}",
			"options": map[string]string{
				"prefix": "book",
			},
			"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
				var in FormBook
				if err := dec(&in); err != nil {
					return nil, err
				}
				return t.Edit(ctx, in)
			},
		},
		{
			"name":     "Delete",
			"resource": "delete|{id}",
			"options": map[string]string{
				"prefix": "book",
			},
			"invoke": func(ctx context.Context, dec func(interface{}) error) (interface{}, error) {
				var in QueryId
				if err := dec(&in); err != nil {
					return nil, err
				}
				return nil, t.Delete(ctx, in)
			},
		},
	}
}

还有基于代码注释可用于运行时自描述服务的文件 zzgen.doc.go

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

package overview01

var (
	ZZ_types_doc = map[interface{}]map[string]string{
		(*BookService)(nil): _doc_BookService,
		(*QueryId)(nil):     _doc_QueryId,
		(*QueryBook)(nil):   _doc_QueryBook,
		(*FormBook)(nil):    _doc_FormBook,
		(*Book)(nil):        _doc_Book,
		(*ListBook)(nil):    _doc_ListBook,
	}

	_doc_BookService = map[string]string{
		"":       "Book manager service",
		"Get":    "Get book entity by id",
		"List":   "Get book entities list by query params",
		"Create": "Create book entity with form",
		"Edit":   "Edit book entity from form",
		"Delete": "Delete book entity by id",
	}

	_doc_QueryId = map[string]string{
		"":   "Uri path id param",
		"Id": "Entity Id",
	}

	_doc_QueryBook = map[string]string{
		"":         "Query book entities param",
		"PageNo":   "Query pagination offser no",
		"PageSize": "Query pagination size",
	}

	_doc_FormBook = map[string]string{
		"":      "Form to edit or create book entity",
		"Title": "Title of book entity",
		"Type":  "Type of book entity",
	}

	_doc_Book = map[string]string{
		"":          "Book entity",
		"CreatedAt": "Book entity create time",
		"CreatedBy": "Book entity create user",
		"UpdatedAt": "Book entity last update time",
		"UpdatedBy": "Book entity last update user",
	}

	_doc_ListBook = map[string]string{
		"":      "Book entities list",
		"Total": "Query pagination total count in database",
		"List":  "Entities list data",
	}
)

提示

生成代码文件同时会在同目录生成代码模版,可对模版修改进行自定义生成

.
├── go.mod
├── types.go
├── zzgen.api.go
├── zzgen.api.go.tmpl
├── zzgen.doc.go
└── zzgen.doc.go.tmpl

在进行Web框架路由绑定之前,我们就可以使用 zzgen.api.gozzgen.doc.go 的信息去直接生成 OpenApi 文档。

可以参考以下的例子:

# 示例二

这个示例展示了一个Web项目的基本组成层级、各层的接口依赖以及依赖配置 示例项目 (opens new window)

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")
}

给它添加上一些注解,大概说明用作构造依赖和或依赖关系:



















 
























 


















 





 







 



















 














 





 









package overview02

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

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

//go:generate gozz run -p "wire" ./
//go:generate sh -c "go run ./cmd/stgragh | dot -Tpng -o structure.png"
//go:generate sh -c "go run ./cmd/stgragh | dot -Tsvg -o structure.svg"

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")
}

完整的依赖构造代码 wire_gen.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
}

并且在 wire_zzaop.go 中生成了为 ServiceHandler 接口提供类型安全静态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-kit (opens new window)ztree 对实例化结构体进行运行时分析,可以生成如下的对象结构图:

# 点击这里开始探索更多 →