必须先注册类型再依赖注入,否则运行时panic;控制器字段注入需满足标签、类型注册、依赖已注册三条件;Scoped依赖工厂函数必须接收iris.Context参数;hero.Handler不支持Scoped依赖。

注册依赖前必须先声明类型,否则运行时 panic
直接在控制器或处理器里 new 一个服务实例,Iris 不会识别它为可注入项——容器根本不知道这个类型存在。调用时会触发 panic: no dependency found for *UserService。这不是配置错误,是注册缺失的硬性报错。
必须在 app.Register 或快捷方法中显式注册:
-
app.Register((*UserService)(nil)):注册接口实现,空指针才能让 Iris 正确反射出底层类型 -
app.Register(func() *sql.DB { return dbConn }):工厂函数注册,适合需要初始化逻辑的依赖 -
app.Singleton[Logger](func() *Logger { return NewLogger() }):明确生命周期,避免误用app.Register默认单例但语义模糊的问题
注意:app.Register(UserService{}) 是错的——传值会导致类型推导失败,字段注入时找不到匹配项。
控制器字段注入需满足三个硬性条件
哪怕类型已注册,UserController 的字段也不会自动填充,除非同时满足:
- 控制器结构体字段带
inject:""标签(不能省略引号) - 该控制器类型本身用
app.RegisterController[UserController]()注册过(不是实例!是类型) - 所有被注入的字段类型(如
*sql.DB、*UserService)已在前一步完成注册
漏掉任一条件,Iris 都会静默跳过注入,字段保持 nil——HTTP 处理器里一解引用就 panic,且无明确提示。调试时建议在控制器 BeginRequest 中加 if c.UserService == nil { panic("UserService not injected") } 快速暴露问题。
Scoped 依赖必须用 Context 参数工厂函数
想让每个 HTTP 请求都获得新实例(比如带请求 ID 的日志器),不能只写 app.Register(NewLogger, iris.ScopeRequest)——Iris 会报错:函数签名不匹配。
Scoped 注册的工厂函数签名强制要求第一个参数是 iris.Context:
app.Scoped[Logger](func(ctx iris.Context) *Logger {
return NewLoggerWithRequestID(ctx)
})
如果写成 func() *Logger,即使加了 iris.ScopeRequest,Iris 也拒绝注册,因为无法把当前请求上下文传进去。这是 Scoped 和 Singleton 最容易混淆的边界:作用域 ≠ 调用时机,而是“能否访问请求上下文”。
hero.Handler 里的依赖绑定和 MVC 是两套独立机制
别指望 app.Register 注册的依赖能自动出现在 hero.Handler 函数参数里。hero 子包走的是另一条路:它靠函数签名反射 + 内置规则匹配(如路径参数、iris.Context、已注册的静态服务),但不会读取 DI 容器里的 Scoped 实例。
例如:
func handleUser(ctx iris.Context, svc *UserService) string {
return svc.GetName()
}
app.Get("/user", hero.Handler(handleUser))
这里 *UserService 能注入,前提是它已被 app.Register 或 app.Singleton 注册为静态依赖;而 ctx 是 hero 自动提供的,和 DI 容器无关。混用时最常踩的坑是:以为 Scoped 注册的日志器也能进 hero 函数——不行,Scoped 只对 MVC 控制器和标准处理器有效。
真正容易被忽略的点是生命周期混用:Singleton 服务在 hero 函数里是安全的,但 Scoped 服务若强行塞进 hero,要么注册失败,要么运行时报 “no dependency found”,因为 hero 不解析 iris.Context 参数以外的 Scoped 工厂。


















