Hyperf注解需扫描、AOP、容器协同生效;漏任一环则静默失败。扫描须显式开启并配对路径,@Inject要求属性可写且类型明确,@Controller与@AutoController逻辑不同不可混用,动态路由需注意通配符规则与性能。

Hyperf 注解不是写完就生效的魔法,它依赖扫描、AOP 和容器三者协同;漏掉任一环,@Inject 不注入、@GetMapping 不注册路由——都是静默失败,连错误都不报。
注解扫描必须显式开启且路径配对
Hyperf 默认不扫描任何目录,config/autoload/annotations.php 里 'scan' => true 是开关,但真正起作用的是 scan.paths。常见错误是只写了 app/Controller,却忘了 app/Service 或 app/Annotation —— 导致自定义注解或 @Inject 的服务类根本没被加载。
- 路径必须是相对于项目根目录的相对路径,不能带
./或../,例如写成app/Controller,不是./app/Controller - 控制器类文件名必须以
Controller结尾(如UserController.php),否则扫描器直接跳过 - 修改
annotations.php后,必须清空runtime/container目录,否则旧扫描结果缓存仍在,新注解不会生效
@Inject 属性注入的限制和写法
@Inject 看似简单,但实际生效要同时满足:属性可写、类型可推导、类已被容器管理。它不支持 private 属性(PHP 反射限制),也不支持未声明类型的混合变量。
- 属性必须是
public或protected,private会静默忽略 - 推荐写法:
#[Inject] private UserService $userService;(PHP 8.0+ 属性注解 + 类型提示),不要用旧式 PHPDoc 风格/** @Inject */ - 若注入接口(如
UserInterface),需先在config/autoload/dependencies.php中绑定实现:UserInterface::class => UserService::class - 别名注入写法:
#[Inject("cache.client")] private Redis $redis;,对应dependencies.php中的别名注册
@Controller 和 @AutoController 别混用
这两个注解底层逻辑完全不同:@AutoController 是“自动挂载所有 public 方法为同名路由”,而 @Controller 是“完全不生成路由,全靠方法上 @GetMapping 等显式声明”。混用会导致路由消失却不报错。
-
@AutoController(prefix: "/api")→ 自动生成GET /api/index、POST /api/create等,适合快速原型 -
@Controller(prefix: "/api")→ 类本身无路由,必须每个方法都加@GetMapping("/users")才注册 - 写了
@Controller却漏了方法级注解?Hyperf 就当这个方法不存在,不会 warn,也不会 404,只是彻底不注册 - 路径拼接规则:
@Controller(prefix: "/v1")+@GetMapping("user/{id}")=/v1/user/{id};注意方法级路径不能以/开头,否则拼接后变成//user/{id},可能被 fast-route 解析异常
动态路径和通配符要小心正则与性能
想匹配类似 /qq_367143/article/details/1494436 这种深度嵌套的路径,@GetMapping("/{*path}") 最直接,但它本质是 fast-route 的 {*path} 通配符,不是正则,也不支持捕获组。
- 精确匹配多段路径,用占位符+正则约束更可控:
@GetMapping("/{user}/article/details/{id:\d+}") -
{*path}会吃掉整个剩余路径,包括斜杠,$path参数值就是qq_367143/article/details/1494436字符串,需手动explode('/', $path) - 通配符路由优先级最高,如果放在通用控制器里,可能意外覆盖其他更具体的路由,建议单独建一个
FallbackController专门处理 - 域名级路由控制不在注解里做,得去
config/autoload/server.php配virtual_hosts,注解只管路径部分
最易被忽略的一点:所有注解类(如 GetMapping、Inject)必须用 use 显式引入,不能只靠 IDE 自动补全——Hyperf 的注解解析器不走自动加载,只认命名空间导入是否完整。

















