Go 无原生装饰器语法,但可通过高阶函数模拟:接收并返回同类型函数,实现日志、认证、重试等横切逻辑;HTTP 中间件是最典型应用,需注意执行顺序、显式转换和调用 next.ServeHTTP。

Go 里没有原生装饰器语法,但可以用函数式组合模拟
Go 不支持 Python 那种 @decorator 语法,也没有类方法装饰器概念。所谓“Go 装饰器模式”,本质是用高阶函数(接收函数并返回新函数)包装行为,常见于中间件、日志、重试、熔断等场景。核心不是语法糖,而是把横切逻辑抽成可复用、可嵌套的函数。
用 func(http.Handler) http.Handler 实现 HTTP 中间件是最典型例子
HTTP 处理链天然适合装饰:每个中间件接收一个 http.Handler,添加逻辑后返回新 http.Handler,最终串成调用链。注意顺序决定执行方向(外层先执行,内层后执行)。
- 必须用
http.HandlerFunc显式转换普通函数为http.Handler - 包装函数里要记得调用
next.ServeHTTP(w, r),否则请求中断 - 多个中间件嵌套时,推荐用辅助函数(如
Chain)避免括号地狱
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("START %s %s", r.Method, r.URL.Path)
next.ServeHTTP(w, r)
log.Printf("END %s %s", r.Method, r.URL.Path)
})
}
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-API-Key") != "secret" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
// 使用:log → auth → handler
handler := loggingMiddleware(authMiddleware(http.HandlerFunc(yourHandler)))
通用函数装饰器需统一签名,否则无法链式组合
HTTP 中间件能链式使用,是因为所有装饰器都接受并返回 http.Handler。如果想包装任意业务函数(比如 func(int, string) error),必须先约定输入输出类型——否则每个装饰器都要单独适配,失去复用价值。
- 推荐定义类型别名,如
type HandlerFunc func(int, string) error - 装饰器函数签名应为
func(HandlerFunc) HandlerFunc - 不建议强行泛化到
interface{}或反射,会丢失类型安全和可读性
type ProcessFunc func(data string) (string, error)func retryOnFailure(maxRetries int) func(ProcessFunc) ProcessFunc { return func(fn ProcessFunc) ProcessFunc { return func(data string) (string, error) { var result string var err error for i := 0; i <= maxRetries; i++ { result, err = fn(data) if err == nil { return result, nil } if i == maxRetries { break } time.Sleep(time.Second * time.Duration(i+1)) } return result, err } } }
// 使用 process := retryOnFailure(2)(realProcess)
容易忽略的坑:闭包变量捕获、panic 传播、context 取消
装饰器里常要加超时、日志、指标等逻辑,但 Go 的闭包和错误处理机制会让某些问题延迟暴露:
立即学习“go语言免费学习笔记(深入)”;
- 在循环中创建装饰器(如批量注册中间件)时,若闭包引用了循环变量,所有装饰器可能共享最后一个值 —— 用局部变量复制解决
- 被包装函数 panic 时,外层装饰器若没 recover,整个 goroutine 崩溃;中间件通常应捕获并转为 HTTP 错误
- HTTP 请求带
context.Context,装饰器若启动 goroutine,必须显式传递并监听ctx.Done(),否则泄漏
实际项目中,越靠近底层(如数据库访问、RPC 调用)的装饰器,越需要严谨处理 context 和错误分类。简单日志或计数可以轻量,但重试、熔断、限流必须考虑并发安全与资源释放。










