Hyperf注解失效主因是路径未配置和缓存未生成:scan.paths未包含自定义目录导致文件不被扫描;SCAN_CACHEABLE=true时需手动执行di:init-proxy生成缓存,否则注解不生效。

scan.paths 漏目录 = 注解根本没被读到
Hyperf 默认只扫描 BASE_PATH . '/app',你把控制器放在 app/Http/Controller、服务类放在 Domain/User、或者模块在 MyApp,只要没显式加进 config/autoload/annotations.php 的 scan.paths,框架连文件名都不会打开——更别说解析注解了。
检查点:
- 路径必须用正斜杠,
app\Controller(Windows 风格反斜杠)是错的,app/Controller也不行,必须写成BASE_PATH . '/app/Controller' - 自定义目录(如
MyApp、Modules)要同步改两处:composer.json的autoload.psr-4+annotations.php的scan.paths - 确认目录真实存在:
ls -l app/Controller(注意大小写、末尾s是否漏了)
PHP 8 Attributes 写法错一点就静默失效
Hyperf 3.0+ 彻底弃用 /** @Controller */ 这类 Doctrine 注释,也完全不识别属性类型提示(如 public UserService $userService;)。错写成注释、漏写 #[Attribute]、作用域不对,都会导致路由 404 或注入为 null,且无任何日志。
正确写法要点:
立即学习“PHP免费学习笔记(深入)”;
-
#[Controller]和#[GetMapping]必须在同一类中,且类名需符合 PSR-4(如HelloController.php→App\Controller\HelloController) - 属性注入必须显式写
#[Inject]:#[Inject] public UserService $userService;,不能只靠类型提示 - 构造函数注入支持命名参数:
public function __construct(#[Inject] UserService $service) { } - 自定义注解类必须继承
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)],不能只写#[Attribute]
SCAN_CACHEABLE 开着但缓存没生成 = 白配置
SCAN_CACHEABLE=true 不是“自动缓存开关”,它只是个条件判断:只有当 runtime/container/annotation/ 下存在合法 PHP 缓存文件时,才会跳过扫描。这些文件不会自己生成,必须手动触发。
关键操作:
- 首次部署或改完
scan.paths后,必须运行:php bin/hyperf.php di:init-proxy - Docker 构建时,
runtime/container/不能被.dockerignore过滤,也不能在ENTRYPOINT里删掉再重跑 - 开发环境建议关掉缓存:
'cacheable' => false,否则改了注解不重启服务就无效 - 即使缓存已存在,只要
config/autoload/annotations.php中'scan' => ['enable' => true],Hyperf 仍会先尝试扫描——这步多余且可能报路径不可读警告
AnnotationCollector 拿不到注解?先看注解类有没有被扫描
自定义注解类(比如权限注解 #[Permission])如果放在 App\Annotation 以外的路径,或者没加进 scan.paths,AnnotationCollector::getAnnotationsByMethod() 就永远返回空数组——不是代码写错了,是注解类压根没被加载。
排查步骤:
- 注解类必须放在
scan.paths包含的目录下(如app/Annotations),并在annotations.php中注册该路径 - 注解类顶部必须有
use Attribute;,且声明#[Attribute(Attribute::TARGET_METHOD)] - 别把注解类和控制器混放——比如放在
App\Controller下,框架扫描时可能因命名空间不匹配直接跳过 - 运行
composer dump-autoload -o确保类能被自动加载,否则di:init-proxy阶段连类都找不到
注解扫描是个两级生效过程:Composer autoloader 要能加载类,Hyperf 的 Finder 才会去读文件内容;两者缺一,注解就等于不存在。最常被忽略的是路径拼接错误和缓存未生成这两步,而不是语法本身。



















