第一个示例
通过完整的示例学习 Wire 的使用流程。
项目结构
目录结构
myapp/
├── cmd/
│ └── server/
│ ├── main.go
│ └── wire.go # Injector 定义
│ └── wire_gen.go # 生成代码
├── internal/
│ ├── config/
│ │ └── config.go
│ ├── data/
│ │ └── database.go
│ │ └ repository.go
│ ├── biz/
│ │ └ service.go
│ └── server/
│ └── handler.go
│ └ server.go
├── go.mod
└── go.sum定义组件
Config
go
package config
type Config struct {
Database DatabaseConfig
Server ServerConfig
}
type DatabaseConfig struct {
Host string
Port int
User string
Password string
Database string
}
type ServerConfig struct {
Port int
}
func NewConfig() *Config {
return &Config{
Database: DatabaseConfig{
Host: "localhost",
Port: 5432,
User: "user",
Password: "password",
Database: "mydb",
},
Server: ServerConfig{
Port: 8080,
},
}
}Database
go
package data
import (
"database/sql"
"fmt"
"myapp/internal/config"
)
type Database struct {
DB *sql.DB
}
func NewDatabase(cfg *config.Config) (*Database, func(), error) {
dsn := fmt.Sprintf(
"host=%s port=%d user=%s password=%s dbname=%s",
cfg.Database.Host,
cfg.Database.Port,
cfg.Database.User,
cfg.Database.Password,
cfg.Database.Database,
)
db, err := sql.Open("postgres", dsn)
if err != nil {
return nil, nil, err
}
if err := db.Ping(); err != nil {
return nil, nil, err
}
cleanup := func() {
db.Close()
}
return &Database{DB: db}, cleanup, nil
}Repository
go
package data
type UserRepository struct {
db *Database
}
func NewUserRepository(db *Database) *UserRepository {
return &UserRepository{db: db}
}
func (r *UserRepository) FindByID(id int64) (*User, error) {
query := "SELECT id, name, email FROM users WHERE id = $1"
row := r.db.DB.QueryRow(query, id)
user := &User{}
err := row.Scan(&user.ID, &user.Name, &user.Email)
if err != nil {
return nil, err
}
return user, nil
}
type User struct {
ID int64
Name string
Email string
}Service
go
package biz
import (
"context"
"errors"
"myapp/internal/data"
)
var ErrUserNotFound = errors.New("用户不存在")
type UserService struct {
repo *data.UserRepository
}
func NewUserService(repo *data.UserRepository) *UserService {
return &UserService{repo: repo}
}
func (s *UserService) GetUser(ctx context.Context, id int64) (*data.User, error) {
user, err := s.repo.FindByID(id)
if err != nil {
return nil, ErrUserNotFound
}
return user, nil
}Handler
go
package server
import (
"encoding/json"
"net/http"
"strconv"
"myapp/internal/biz"
"myapp/internal/config"
)
type UserHandler struct {
service *biz.UserService
cfg *config.Config
}
func NewUserHandler(service *biz.UserService, cfg *config.Config) *UserHandler {
return &UserHandler{
service: service,
cfg: cfg,
}
}
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
http.Error(w, "invalid id", http.StatusBadRequest)
return
}
user, err := h.service.GetUser(r.Context(), id)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
json.NewEncoder(w).Encode(user)
}Server
go
package server
import (
"net/http"
"myapp/internal/config"
)
type Server struct {
httpServer *http.Server
handler *UserHandler
}
func NewServer(cfg *config.Config, handler *UserHandler) *Server {
mux := http.NewServeMux()
mux.HandleFunc("/user", handler.GetUser)
httpServer := &http.Server{
Addr: ":" + strconv.Itoa(cfg.Server.Port),
Handler: mux,
}
return &Server{
httpServer: httpServer,
handler: handler,
}
}
func (s *Server) Start() error {
return s.httpServer.ListenAndServe()
}
func (s *Server) Stop() error {
return s.httpServer.Shutdown(context.Background())
}定义 Provider Set
按层组织
go
package data
import "github.com/google/wire"
var DataSet = wire.NewSet(
NewDatabase,
NewUserRepository,
)go
package biz
import "github.com/google/wire"
var BizSet = wire.NewSet(
NewUserService,
)go
package server
import "github.com/google/wire"
var ServerSet = wire.NewSet(
NewUserHandler,
NewServer,
)定义 Injector
wire.go
go
//+build wireinject
package main
import (
"github.com/google/wire"
"myapp/internal/biz"
"myapp/internal/config"
"myapp/internal/data"
"myapp/internal/server"
)
func InitializeServer() (*server.Server, func(), error) {
wire.Build(
config.NewConfig,
data.DataSet,
biz.BizSet,
server.ServerSet,
)
return nil, nil, nil
}生成代码
运行 wire
bash
cd cmd/server
wire生成的 wire_gen.go
go
// Code generated by Wire. DO NOT EDIT.
//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
//+build !wireinject
package main
import (
"myapp/internal/biz"
"myapp/internal/config"
"myapp/internal/data"
"myapp/internal/server"
)
func InitializeServer() (*server.Server, func(), error) {
config := config.NewConfig()
database, cleanup, err := data.NewDatabase(config)
if err != nil {
return nil, nil, err
}
userRepository := data.NewUserRepository(database)
userService := biz.NewUserService(userRepository)
userHandler := server.NewUserHandler(userService, config)
server := server.NewServer(config, userHandler)
return server, func() {
cleanup()
}, nil
}使用生成的代码
main.go
go
package main
import (
"log"
"os"
"os/signal"
"syscall"
)
func main() {
server, cleanup, err := InitializeServer()
if err != nil {
log.Fatal(err)
}
defer cleanup()
// 启动服务器
go func() {
if err := server.Start(); err != nil {
log.Printf("server error: %v", err)
}
}()
// 等待中断信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
// 停止服务器
if err := server.Stop(); err != nil {
log.Printf("server shutdown error: %v", err)
}
}运行项目
编译运行
bash
go build -o server ./cmd/server
./server测试
bash
curl http://localhost:8080/user?id=1依赖图可视化
使用 wire check
bash
wire check ./cmd/server输出:
config.Config
data.Database
data.UserRepository
biz.UserService
server.UserHandler
server.Server添加新组件
添加新服务
go
package biz
type OrderService struct {
repo *data.OrderRepository
}
func NewOrderService(repo *data.OrderRepository) *OrderService {
return &OrderService{repo: repo}
}
var BizSet = wire.NewSet(
NewUserService,
NewOrderService, // 新增
)更新 Injector
go
func InitializeServer() (*server.Server, func(), error) {
wire.Build(
config.NewConfig,
data.DataSet,
biz.BizSet,
server.ServerSet,
)
return nil, nil, nil
}重新生成
bash
wire ./cmd/server错误处理示例
Provider 返回错误
go
func NewDatabase(cfg *config.Config) (*Database, func(), error) {
if cfg.Database.Host == "" {
return nil, nil, errors.New("database host is required")
}
// ...
}Injector 处理错误
go
func main() {
server, cleanup, err := InitializeServer()
if err != nil {
log.Fatalf("初始化失败: %v", err)
}
defer cleanup()
// ...
}完整流程总结
- 定义组件:编写业务组件和 Provider 函数
- 组织 Provider Set:按层分组组织 Provider
- 定义 Injector:使用 wire.Build 声明依赖
- 生成代码:运行 wire 生成 wire_gen.go
- 使用生成代码:在 main.go 中调用 Injector
- 测试运行:编译并运行应用