Hyperf 3.1 注解不生效主因是未被扫描到,需检查 scan.paths 是否包含实际路径、执行 di:init-proxy 生成缓存、禁用 SCAN_CACHEABLE 或确保 runtime/container/annotation/ 存在有效文件,并正确使用 PHP 8 原生 Attributes 语法。

Hyperf 3.1 注解不生效,90% 是因为注解压根没被扫描到——即使 #[Controller] 写得完全正确,只要 scan.paths 漏了目录、di:init-proxy 没执行、或 SCAN_CACHEABLE=true 但缓存文件缺失,路由就 404,且无任何报错日志。
确认注解类是否在扫描路径内
打开 config/autoload/annotations.php,检查 scan.paths 数组是否显式包含你的控制器、服务、自定义注解所在目录。Hyperf 默认只扫 BASE_PATH . '/app',如果你把 Controller 放在 app/Http/Controller、Service 放在 app/Domain/User、或自定义注解放在 App/Annotation,这些路径必须手动加进去。
错误写法:'paths' => ['app/Controller'](相对路径、漏 BASE_PATH、大小写不一致)
正确写法:'paths' => [BASE_PATH . '/app', BASE_PATH . '/app/Http/Controller', BASE_PATH . '/app/Domain', BASE_PATH . '/App/Annotation']
【Windows 用户务必用正斜杠】:appController 是错的,必须写成 BASE_PATH . '/app/Controller'。
改完后别跳过这步:运行 composer dump-autoload -o,否则 PHP 根本加载不到这些类,扫描器连文件名都不会读。
强制刷新注解缓存
首次修改 scan.paths 或新增注解类后,必须执行:
php bin/hyperf.php di:init-proxy
该命令会清空 runtime/container/ 目录,并重新扫描所有路径、解析注解、生成代理类和元数据缓存文件。
如果 SCAN_CACHEABLE=true(默认开启),但 runtime/container/annotation/ 下没有合法的 .php 缓存文件,Hyperf 就会静默跳过扫描——不是报错,是直接当没这回事。
Docker 构建时,确保 .dockerignore 没过滤 runtime/container/;ENTRYPOINT 中禁止执行 rm -rf runtime/container/ 后再启动服务。
验证注解是否真正被收集
方法一:用 AnnotationCollector 手动查
在任意 Command 或 Listener 中插入以下代码并执行:
var_dump(AnnotationCollector::getAnnotationsByClass(App\Controller\HelloController::class));
如果返回空数组,说明扫描链已断——优先检查 scan.paths 和 di:init-proxy 是否执行。
方法二:确认类文件名与 PSR-4 命名严格匹配
例如类名 AppControllerHelloController 必须对应文件 app/Controller/HelloController.php;若实际文件是 app/Controller/hellocontroller.php 或 Hellocontroller.php,Hyperf 无法自动加载,注解自然不会被识别。
方法三:临时关闭缓存快速验证
在 annotations.php 中设 'cacheable' => false,重启服务。此时每次请求都会实时扫描,改完注解无需重跑 di:init-proxy,适合开发调试。
PHP 8 Attributes 写法避坑
第一步:彻底弃用 Doctrine 风格注释
/** @Controller */ 在 Hyperf 3.0+ 已无效,必须改用 PHP 8 原生 Attributes。
第二步:#[Controller] 和 #[GetMapping] 必须写在同一类中
不能把 #[Controller] 放父类、#[GetMapping] 放子类——Hyperf 不继承注解,子类需单独声明 #[Controller]。
第三步:属性注入必须显式注解
PHP 8 的类型提示 public UserService $service; 不会被识别,必须写成 #[Inject] public UserService $service;。
【注解类本身也必须被扫描】:自定义注解如 AppAnnotationPermission,必须放在 scan.paths 包含的目录下,且命名空间与文件路径严格对应。


















