如何在Go的Wire依赖注入框架中为不同提供者配置专属zap.Logger实例
Google Wire 同类型多实例注入与运行时参数融合方案
Wire 基于类型匹配依赖,要实现同类型不同实例注入、以及编译期配置和运行时变量的融合,可按以下步骤实现:
核心思路
- 对同类型的不同依赖,通过类型别名做唯一标识,解决Wire按类型匹配的冲突问题
- 运行时生成的变量(根日志、配置、密钥等)直接作为注入器的入参传入,Wire会自动将其纳入依赖树
- 通过适配层将别名类型转换为原有工厂需要的原生类型,无需修改业务代码
具体实现步骤
1. 定义类型别名区分Logger
为每个服务需要的Logger定义独立的类型别名:
// 放在wire配置文件中 type UserDaoLogger *zap.Logger type JWTServiceLogger *zap.Logger type LoginServiceLogger *zap.Logger
2. 编写Logger适配Provider
将运行时传入的根Logger转换为对应别名类型的带命名的Logger实例:
func provideUserDaoLogger(root *zap.Logger) UserDaoLogger { return UserDaoLogger(root.Named("dao.user")) } func provideJWTServiceLogger(root *zap.Logger) JWTServiceLogger { return JWTServiceLogger(root.Named("service.jwt")) } func provideLoginServiceLogger(root *zap.Logger) LoginServiceLogger { return LoginServiceLogger(root.Named("service.login")) }
3. 更新ProviderSet
保留原有绑定和工厂,新增适配Provider和参数绑定:
var AppSet = wire.NewSet( // 原有接口绑定 wire.Bind(new(interfaces.LoginService), new(*service.LoginServiceImpl)), wire.Bind(new(interfaces.JWTService), new(*service.JWTServiceImpl)), wire.Bind(new(interfaces.UserDao), new(*dao.UserDaoImpl)), // 原有工厂 service.NewLoginService, service.NewJWTService, dao.NewUserDao, database.Connect, // 新增Logger适配Provider provideUserDaoLogger, provideJWTServiceLogger, provideLoginServiceLogger, // 给指定工厂绑定对应Logger(无需修改原有工厂代码) wire.Bind(new(*zap.Logger), new(UserDaoLogger)).For(dao.NewUserDao), wire.Bind(new(*zap.Logger), new(JWTServiceLogger)).For(service.NewJWTService), wire.Bind(new(*zap.Logger), new(LoginServiceLogger)).For(service.NewLoginService), )
如果你的Wire版本不支持.For()参数绑定,也可以写包装工厂替代:
func wrapNewUserDao(db *gorm.DB, logger UserDaoLogger) interfaces.UserDao { return dao.NewUserDao(db, (*zap.Logger)(logger)) } // 将ProviderSet中的dao.NewUserDao替换为wrapNewUserDao即可
4. 定义注入器函数
将所有运行时参数作为注入器的入参:
//go:build wireinject // +build wireinject package yourpkg import "github.com/google/wire" func InitializeApp( mysqlConfig interfaces.MySQLConfig, // 运行时加载的数据库配置 rootLogger *zap.Logger, // 运行时创建的根日志 jwtKey [32]byte, // 运行时读取的JWT密钥 ) (interfaces.LoginService, error) { wire.Build(AppSet) return nil, nil }
5. 调用注入器
在入口main函数中传入运行时生成的变量即可:
func main() { // 运行时创建根日志 logger, err := zap.NewDevelopment() if err != nil { panic(err) } defer logger.Sync() // 运行时加载配置 mysqlConfig := loadMySQLConfig() jwtKey := loadJWTKey() // 直接调用Wire生成的注入器,自动完成所有依赖组装 loginService, err := InitializeApp(mysqlConfig, logger, jwtKey) if err != nil { panic(err) } // 业务逻辑使用loginService即可 }
内容的提问来源于stack exchange,提问作者Naftuli Kay
相关产品推荐
相关产品推荐

