golang-open-source-projects
golang-open-source-projects copied to clipboard
【开源自荐】 Gozz 强大的注解分析及代码生成工具 提供 依赖注入 AOP API代码生成等
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 还提供了运行时库的工具 ,可以协助我们对对象进行运行时分析,并输出架构图