func newLogger(prefix string) func(string) {
// 函数体
return func(msg string) {
now := time.Now().Format("2006-01-02 15:04:05")
fmt.Printf("[%s] [%s] %s\n", now, prefix, msg)
}
}
func newLogger(prefix string) 定义一个名为 newLogger 的函数,入参是 prefix(字符串类型),代表 “日志前缀(模块名)”
func(string) 这个函数的返回值类型是「一个函数」—— 具体是 “接收一个字符串参数、无返回值的函数”
return func(msg string) { ... } 返回一个「匿名函数」(没有名字的函数),这个匿名函数就是最终用来打日志的函数
msg string:这个匿名函数的入参,是我们要打印的具体日志内容(比如 “创建订单成功”);
关键:这个匿名函数 “捕获” 了外部函数 newLogger 的变量 prefix —— 这就是闭包的核心:内部函数能访问外部函数的变量,哪怕外部函数执行完了,内部函数依然能记住这个变量的值。
// 扩展:给中间件加“耗时阈值”,超过阈值打印警告
func CostTimeMiddleware(threshold time.Duration) gin.HandlerFunc {
// 闭包捕获外部变量 threshold(阈值)
return func(c *gin.Context) {
start := time.Now()
c.Next()
cost := time.Since(start)
// 用捕获的 threshold 判断是否告警
if cost > threshold {
fmt.Printf("警告:接口%s耗时%v,超过阈值%v\n", c.Request.URL.Path, cost, threshold)
} else {
fmt.Printf("接口%s耗时:%v\n", c.Request.URL.Path, cost)
}
}
}
// 注册时传入阈值 50ms
r.Use(CostTimeMiddleware(50 * time.Millisecond))
r := gin.Default() 创建 Gin 的 “核心引擎”,自带两个基础中间件:
① 日志中间件:打印请求日志;
② 恢复中间件:接口 panic 时不崩溃,返回 500 错误
r.Use(CostTimeMiddleware()) 给 Gin 引擎注册我们写的耗时统计中间件 ——所有接口都会先经过这个中间件
r.GET("/hello", func(c *gin.Context) { ... }) 定义一个 GET 接口:
① 路径:/hello;
② 处理函数:匿名函数,里面是接口的业务逻辑
time.Sleep(100 * time.Millisecond) 模拟业务耗时(比如查数据库需要 100ms)
c.JSON(200, gin.H{"msg": "hello"}) 给客户端返回 JSON 响应:
① 200:HTTP 状态码(成功);
② gin.H:Gin 简化的 map 类型,等价于 map[string]interface{}
r.Run(":8080") 启动服务,监听本机 8080 端口 —— 运行后就能通过 http://localhost:8080/hello 访问
// 闭包封装事务逻辑:执行完业务后自动提交/回滚
func withTransaction(db sql.DB, fn func(sql.Tx) error) error {
tx, err := db.Begin()
if err != nil {
return err
}
// 闭包捕获tx,延迟执行回滚/提交
defer func() {if r := recover(); r != nil {tx.Rollback() // 恐慌时回滚}
}()// 执行业务逻辑(传入事务对象)
if err := fn(tx); err != nil {tx.Rollback() // 业务出错回滚return err
}return tx.Commit() // 业务成功提交
}
// 使用示例:
func main() {
db, _ := sql.Open("mysql", "dsn")
// 闭包传递业务逻辑,withTransaction负责事务管理
err := withTransaction(db, func(tx *sql.Tx) error {
// 业务逻辑:插入数据
_, err := tx.Exec("INSERT INTO user (name) VALUES (?)", "张三")
return err
})
if err != nil {
fmt.Println("事务失败:", err)
}
}