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 同步
interface和implement - Option 生成
Funcitonal Options風格代碼 - Orm 生成數據實體映射代碼
- Tag 模版化管理結構體字段標籤
- Wire 自動化依賴注入 及 AOP代理
更多有趣的使用可以訪問 Github 探索,也歡迎提 Issue 和 Star