使用wire重构商品微服务
一.wire简介
Wire 是一个轻巧的Golang依赖注入工具。它由Go Cloud团队开发,通过自动生成代码的方式在编译期完成依赖注入。
依赖注入是保持软件 “低耦合、易维护” 的重要设计准则之一。
此准则被广泛应用在各种开发平台之中,有很多与之相关的优秀工具。
其中最著名的当属 Spring,Spring IOC 作为框架的核心功能对Spring的发展到今天统治地位起了决定性作用。
依赖注入很重要,所以Golang社区中早已有人开发了相关工具, 比如来自Uber 的 dig 、来自Facebook 的 inject 。他们都通过反射机制实现了运行时依赖注入。
二.快速使用
2.1安装
安装很简单,运行 go get github.com/google/wire/cmd/wire 之后, wire 命令行工具 将被安装到 $GOPATH/bin 。只要确保 $GOPATH/bin 在 $PATH 中, wire 命令就可以在任何目录调用了。安装成功后运行如下命令

2.2快速入门
设计一个程序,其中 Event依赖Greeter,Greeter依赖Message
package mainimport ("fmt""github.com/pkg/errors""time"
)type Message stringfunc NewMessage(phrase string) Message {return Message(phrase)
}type Greeter struct {Message Message
}func NewGreeter(m Message) Greeter {return Greeter{Message: m}
}func (g Greeter) Greet() Message {return g.Message
}type Event struct {Greeter Greeter // <- adding a Greeter field
}func NewEvent(g Greeter) (Event, error) {if time.Now().Unix()%2 == 0 {return Event{}, errors.New("could not create event: event greeter is grumpy")}return Event{Greeter: g}, nil
}func (e Event) Start() {msg := e.Greeter.Greet()fmt.Println(msg)
}
如果运行Event需要逐个构建依赖,代码如下
func main() {message := NewMessage("lisus2000")greeter := NewGreeter(message)event := NewEvent(greeter)event.Start()
}
在此之前先介绍两个概念
Provider
Provider 你可以把它理解成工厂函数,这个函数的入参是依赖的属性,返回值为新一个新的类型实例
如下所示都是 provider 函数,在实际使用的时候,往往是一些简单的工厂函数,这个函数不会太复杂。
func NewMessage() Message {return Message("Hi there!")
}func NewGreeter(m Message) Greeter {return Greeter{Message: m}
}
Injector
我们常常在 wire.go 文件中定义 injector ,injector也是一个普通函数,它用来声明组件之间的依赖关系
如下代码,我们把Event、Greeter、Message 的工厂函数(provider)一股脑塞入wire.Build()中,代表着构建 Event依赖Greeter、Message。我们不必关心Greeter、Message之间的依赖关系,wire会帮我们处理
func InitializeEvent() Event {wire.Build(NewEvent, NewGreeter, NewMessage)return Event{}
}
编写wire.go文件
//go:build wireinject
// +build wireinjectpackage mainimport "github.com/google/wire"var wireSet = wire.NewSet(wire.Struct(new(Greeter), "Message"), NewMessage)func InitializeEvent(phrase string) (Event, error) {//我们常常在 wire.go 文件中定义 injector ,injector也是一个普通函数,它用来声明组件之间的依赖关系//如下代码,我们把Event、Greeter、Message 的工厂函数(provider)一股脑塞入wire.Build()中,//代表着构建 Event依赖Greeter、Message。我们不必关心Greeter、Message之间的依赖关系,wire会帮我们处理panic(wire.Build(NewEvent, wireSet))//return Event{}, nil
}
注:使用wire生成代码时,要在代码上加以下
可以在wire.go第一行加入 //+build wireinject (与//go:build wireinject等效)注释,确保了这个文件在我们正常编译的时候不会被引用
在带有wire.go目录下运行wire命令,就会生成wire_gen.go文件,如下图所示


完整的main.go文件如下
package mainimport ("fmt""github.com/pkg/errors""time"
)type Message stringfunc NewMessage(phrase string) Message {return Message(phrase)
}type Greeter struct {Message Message
}func NewGreeter(m Message) Greeter {return Greeter{Message: m}
}func (g Greeter) Greet() Message {return g.Message
}type Event struct {Greeter Greeter // <- adding a Greeter field
}func NewEvent(g Greeter) (Event, error) {if time.Now().Unix()%2 == 0 {return Event{}, errors.New("could not create event: event greeter is grumpy")}return Event{Greeter: g}, nil
}func (e Event) Start() {msg := e.Greeter.Greet()fmt.Println(msg)
}func main() {event, _ := InitializeEvent("hello")event.Start()
}
wire的进阶,详见https://blog.csdn.net/weixin_50071922/article/details/133278161
三.基于wire构建商品微服务
3.1编写商品微服务提供者
func NewGoodsAppWire(logOpts *log.Options, registrar registry.Registrar, serverOpts *options.ServerOptions,rpcServer *rpcserver.Server) (*gapp.App, error) {//初始化loglog.Init(logOpts)defer log.Flush()return gapp.New(gapp.WithName(serverOpts.Name),gapp.WithRPCServer(rpcServer),gapp.WithRegistrar(registrar),), nil
}
func NewRegistrar(registry *options.RegistryOptions) registry.Registrar {c := api.DefaultConfig()c.Address = registry.Addressc.Scheme = registry.Schemeclient, err := api.NewClient(c)if err != nil {panic(err)}return consul.New(client, consul.WithHealthCheck(true))
}
func NewGoodsRPCServerWire(telemetry *options.TelemetryOptions,serverOpts *options.ServerOptions, gserver gpb.GoodsServer) (*rpcserver.Server, error) {// 初始化 open-telemetry 的 exportertrace.InitAgent(trace.Options{Name: telemetry.Name,Endpoint: telemetry.Endpoint,Sampler: telemetry.Sampler,Batcher: telemetry.Batcher,})// 这里会根据 endpoint 为单元注册 trace 服务的实例rpcAddr := fmt.Sprintf("%s:%d", serverOpts.Host, serverOpts.Port)var opts []rpcserver.ServerOptionopts = append(opts, rpcserver.WithAddress(rpcAddr))if serverOpts.EnableLimit {opts = append(opts, rpcserver.WithUnaryInterceptor(grpc.NewUnaryServerInterceptor()))}grpcServer := rpcserver.NewServer(opts...)gpb.RegisterGoodsServer(grpcServer.Server, gserver)return grpcServer, nil
}
func NewGoodsServerWire(srv v1.ServiceFactory) proto.GoodsServer {return &goodsServer{srv: srv,}
}
func NewGoodsServiceWire(data v1.DataFactory, dataSearch v12.SearchFactory) ServiceFactory {return &service{data: data,dataSearch: dataSearch,}
}
func GetDBFactoryOr(mysqlOpts *options.MySQLOptions) (v1.DataFactory, error) {if mysqlOpts == nil && dbFactory == nil {return nil, errors.WithCode(code.ErrConnectDB, "failed to get mysql store factory")}var err erroronce.Do(func() {dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",mysqlOpts.Username,mysqlOpts.Password,mysqlOpts.Host,mysqlOpts.Port,mysqlOpts.Database)//希望大家自己可以去封装loggernewLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)logger.Config{SlowThreshold: time.Second, // 慢 SQL 阈值LogLevel: logger.LogLevel(mysqlOpts.LogLevel), // 日志级别IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误Colorful: false, // 禁用彩色打印},)db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: newLogger,})if err != nil {return}sqlDB, _ := db.DB()dbFactory = &mysqlFactory{db: db,}//允许连接多少个mysqlsqlDB.SetMaxOpenConns(mysqlOpts.MaxOpenConnections)//允许最大的空闲的连接数sqlDB.SetMaxIdleConns(mysqlOpts.MaxIdleConnections)//重用连接的最大时长sqlDB.SetConnMaxLifetime(mysqlOpts.MaxConnectionLifetime)})if dbFactory == nil || err != nil {return nil, errors.WithCode(code.ErrConnectDB, "failed to get mysql store factory")}return dbFactory, nil
}
func GetSearchFactoryOr(opts *options.EsOptions) (v12.SearchFactory, error) {if opts == nil && searchFactory == nil {return nil, errors.New("failed to get es client")}once.Do(func() {esOpt := db.EsOptions{Host: opts.Host,Port: opts.Port,}esClient, err := db.NewEsClient(&esOpt)if err != nil {return}searchFactory = &dataSearch{esClient: esClient,}})if searchFactory == nil {return nil, errors.New("failed to get es client")}return searchFactory, nil
}
3.2编写wire.go文件
//go:build wireinject
// +build wireinjectpackage srvimport ("github.com/google/wire"v1 "mxshop/app/goods/srv/internal/controller/v1""mxshop/app/goods/srv/internal/data/v1/data_search/v1/es""mxshop/app/goods/srv/internal/data/v1/db"v12 "mxshop/app/goods/srv/internal/service/v1""mxshop/app/pkg/options"gapp "mxshop/gmicro/app""mxshop/pkg/log"
)func initApp(*log.Options, *options.ServerOptions, *options.RegistryOptions,*options.TelemetryOptions, *options.MySQLOptions, *options.EsOptions) (*gapp.App, error) {wire.Build(NewGoodsAppWire,NewRegistrar,NewGoodsRPCServerWire,v1.NewGoodsServerWire,v12.NewGoodsServiceWire,db.GetDBFactoryOr,es.GetSearchFactoryOr,)return &gapp.App{}, nil
}
3.3生成wire_gen.go文件

生成后的文件如下:
// Code generated by Wire. DO NOT EDIT.//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinjectpackage srvimport (v1_2 "mxshop/app/goods/srv/internal/controller/v1""mxshop/app/goods/srv/internal/data/v1/data_search/v1/es""mxshop/app/goods/srv/internal/data/v1/db""mxshop/app/goods/srv/internal/service/v1""mxshop/app/pkg/options""mxshop/gmicro/app""mxshop/pkg/log"
)// Injectors from wire.go:func initApp(logOptions *log.Options, serverOptions *options.ServerOptions, registryOptions *options.RegistryOptions, telemetryOptions *options.TelemetryOptions, mySQLOptions *options.MySQLOptions, esOptions *options.EsOptions) (*app.App, error) {registrar := NewRegistrar(registryOptions)dataFactory, err := db.GetDBFactoryOr(mySQLOptions)if err != nil {return nil, err}searchFactory, err := es.GetSearchFactoryOr(esOptions)if err != nil {return nil, err}serviceFactory := v1.NewGoodsServiceWire(dataFactory, searchFactory)goodsServer := v1_2.NewGoodsServerWire(serviceFactory)server, err := NewGoodsRPCServerWire(telemetryOptions, serverOptions, goodsServer)if err != nil {return nil, err}appApp, err := NewGoodsAppWire(logOptions, registrar, serverOptions, server)if err != nil {return nil, err}return appApp, nil
}
在如下方法替换接口

运行main.go文件,如下显示,正常启动

相关文章:
使用wire重构商品微服务
一.wire简介 Wire 是一个轻巧的Golang依赖注入工具。它由Go Cloud团队开发,通过自动生成代码的方式在编译期完成依赖注入。 依赖注入是保持软件 “低耦合、易维护” 的重要设计准则之一。 此准则被广泛应用在各种开发平台之中,有很多与之相关的优秀工…...
大三上实训内容
项目一:爬取天气预报数据 【内容】 在中国天气网(http://www.weather.com.cn)中输入城市的名称,例如输入信阳,进入http://www.weather.com.cn/weather1d/101180601.shtml#input 的网页显示信阳的天气预报,其中101180601是信阳的…...
IOT安全学习路标
1. 物联网基础知识 首先,你需要建立坚实的物联网基础知识,包括IoT的架构和组件,传感器和设备的连接和通信技术,云端和边缘计算等。 2. 通信和网络安全 学习关于物联网通信和网络安全的基础知识,包括加密和认证技术、…...
java中线程的状态是如何转换的?
在 Java 中,线程有几种状态,主要包括 NEW(新建)、RUNNABLE(可运行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(计时等待)、和 TE…...
处理合并目录下的Excel文件数据并指定列去重
处理合并目录下的Excel文件数据并指定列去重 需求:读取指定目录下的Excel文件并给数据做合并与去重处理 Python代码实现 import os import pandas as pd import warnings import time from tqdm import tqdm #进度条展示def read_excel(path):dfs []for file in…...
Numpy数组的去重 np.unique()(第15讲)
Numpy数组的去重 np.unique()(第15讲) 🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…...
ROS-log功能区别
ROS使用rosout包来记录各个节点的log信息,通常这些log信息是一些可以读懂的字符串信息,这些信息一般用来记录节点的运行状态。 ROS有五种不同类型的log信息,分别为:logdebug、loginfo、logwarn、logerr、logfatal。 等级由低到高&…...
学习git后,真正在项目中如何使用?
文章目录 前言下载和安装Git克隆远程仓库PyCharm链接本地Git创建分支修改项目工程并提交到本地仓库推送到远程仓库小结 前言 网上学习git的教程,甚至还有很多可视化很好的git教程,入门git也不是什么难事。但我发现,当我真的要从网上克隆一个…...
Qt国际化翻译Linguist使用
QT的国际化是非常方便的,简单的说就是QT有自带的翻译工具把我们源代码中的字符串翻译成任何语言文件,再把这个语言文件加载到项目中就可以显示不同的语言。下面直接上手: 步骤一:打开pro文件,添加:TRANSLA…...
ShardingSphere数据分片之分表操作
1、概述 Apache ShardingSphere 是一款分布式的数据库生态系统, 可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。 Apache ShardingSphere 设计哲学为 Database Plus,旨在构建异构数据库上…...
基于ssm鲸落文化线上体验馆论文
摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本鲸落文化线上体验馆就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信…...
LeetCode Hot100 131.分割回文串
题目: 给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正着读和反着读都一样的字符串。 方法:灵神-子集型回溯 假设每对相邻字符之间有个逗号,那么就看…...
SAP UI5 walkthrough step9 Component Configuration
在之前的章节中,我们已经介绍完了MVC的架构和实现,现在我们来讲一下,SAPUI5的结构 这一步,我们将所有的UI资产从index.html里面独立封装在一个组件里面 这样组件就变得独立,可复用了。这样,无所什么时候我…...
【数据结构和算法】--- 栈
目录 栈的概念及结构栈的实现初始化栈入栈出栈其他一些栈函数 小结栈相关的题目 栈的概念及结构 栈是一种特殊的线性表。相比于链表和顺序表,栈只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的…...
CentOS7.0 下rpm安装MySQL5.5.60
下载 下载路径: MySQL :: Download MySQL Community Server -->looking for the latest GA version-->5.5.60 此压缩包中有多个rpm包 有四个不是必须的,只需安装这三个 MySQL-server-5.5.60-1.el6.x86_64 MySQL-devel-5.5.60-1.el6.x86_64 MySQL-client-5.5.60-1.el6.x8…...
智慧能源:数字孪生压缩空气储能管控平台
压缩空气储能在解决可再生能源不稳定性和提供可靠能源供应方面具有重要的优势。压缩空气储能,是指在电网负荷低谷期将电能用于压缩空气,在电网负荷高峰期释放压缩空气推动汽轮机发电的储能方式。通过提高能量转换效率、增加储能密度、快速启动和调节能力…...
【链表OJ—反转链表】
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 1、反转链表题目: 2、方法讲解: 解法一: 解法二: 总结 前言 世上有两种耀眼的光芒,一种是正在升起的太…...
TCP一对一聊天
客户端 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io…...
基于Java的招聘系统的设计与实现
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…...
spring boot整合mybatis进行部门管理管理的增删改查
部门列表查询: 功能实现: 需求:查询数据库表中的所有部门数据,展示在页面上。 准备工作: 准备数据库表dept(部门表),实体类Dept。在项目中引入mybatis的起步依赖,mysql的…...
国产半导体测试设备公司领军者,杭州加速科技引领产业自主可控新征程
在半导体产业国产化全面推进的背景下,国产半导体测试设备公司成为突破产业链瓶颈、保障中国芯安全的核心力量。半导体测试设备作为芯片制造关键装备,长期依赖进口,制约国内半导体产业发展。经过多年技术攻坚,一批优质国产半导体测…...
**发散创新:策略即代码——用 Rust实现动态权限控制引擎**在现代软件系统中,权限管理早已不是简单的“用
发散创新:策略即代码——用 Rust 实现动态权限控制引擎 在现代软件系统中,权限管理早已不是简单的“用户-角色-资源”映射。越来越多的业务场景要求我们具备灵活、可扩展、易维护的权限决策机制。传统硬编码方式难以应对频繁变更的业务规则,而…...
租车宝 token、payload算法分析
声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 部分python代码 url "/queryOr…...
CH347的JTAG模式怎么选?实测F/T型号在openFPGALoader下的速度与兼容性差异
CH347F与CH347T JTAG模式深度评测:openFPGALoader下的实战性能差异 当你在淘宝搜索"CH347模块"时,会发现两种主要型号:F型多功能版和T型切换版。价格相差无几,但商家描述往往含糊其辞。作为FPGA开发者,最关…...
从特效 SDK 到 AI 动效平台:Neon Vibe Motion 的技术演进之路
多媒体中台在 B 站主要负责剪辑、拍摄、直播等业务场景的动效渲染,开发维护的 SDK 在后文统一称为特效 SDK。 传统的视频特效生产一般分三条链路: 三条链路存在一个困境:效果丰富度、实时可交互、生产效率,三者不可兼得。 那么能…...
用STM32和示波器搞定美的/格力空调红外遥控(附完整C代码)
STM32实战:从示波器捕获到空调红外协议逆向全解析 红外遥控技术看似简单,却蕴含着精妙的时序设计和协议逻辑。作为一名长期混迹于硬件开发领域的工程师,我经常遇到需要逆向控制家电的场景。最近在智能家居项目中,就遇到了需要通过…...
Oracle日期处理进阶:除了EXTRACT,这些场景你还可以试试INTERVAL和TO_CHAR
Oracle日期处理进阶:解锁INTERVAL与TO_CHAR的高阶应用场景 在Oracle数据库的日常开发中,日期时间处理是每个开发者都无法回避的课题。当我们已经熟练掌握了EXTRACT这类基础函数后,往往会发现单纯提取日期部分已经无法满足复杂业务场景的需求—…...
Python内存监控体系搭建:Prometheus+Custom Metrics+内存火焰图,实现OOM前15分钟精准预警
第一章:Python智能体内存管理策略 Python智能体(如基于LLM的Agent、ReAct架构或Tool-Calling Agent)在运行过程中频繁创建临时对象、缓存推理上下文、序列化工具调用结果,导致内存压力显著高于常规脚本。其内存管理需兼顾GC效率、…...
WMatrix 7语料库分析工具上线:隐喻识别高效精准,语言学研究利器
温馨提示:文末有联系方式WMatrix 7:专为语料库驱动隐喻分析优化的实用工具 WMatrix 7是当前广受语言学研究者青睐的语料库分析平台,内置强大词性标注、搭配提取与语义域分类功能,尤其在隐喻识别(如MVU框架适配…...
Libre Barcode:终极免费条码字体解决方案,让条码生成变得简单高效
Libre Barcode:终极免费条码字体解决方案,让条码生成变得简单高效 【免费下载链接】librebarcode Libre Barcode: barcode fonts for various barcode standards. 项目地址: https://gitcode.com/gh_mirrors/li/librebarcode Libre Barcode 是一个…...
