ThinkPHP 8 注解路由需手动启用:先安装 topthink/think-annotation,再调用 AnnotationRoute::init();仅方法级注解有效,须 public 且位于 app/controller/ 下;@Get/@Post 是快捷注解,@Route 支持 middleware 等高级参数;注解解析依赖缓存,修改后需执行 php think clear:route 或重启服务。

注解路由在 ThinkPHP 8 中默认不启用
ThinkPHP 8 的注解路由不是开箱即用的功能,必须手动开启并注册解析器,否则 @Route、@Get 这类注解完全被忽略,控制器方法不会被自动注册为路由 —— 这是绝大多数人卡住的第一步。
实操上要两步到位:
- 确保已安装
topthink/think-annotation(TP8.0+ 默认未内置,需composer require topthink/think-annotation) - 在
app/bootstrap.php或服务提供者中显式调用AnnotationRoute::init()
漏掉任一环节,php think route:list 就看不到注解定义的路由,也收不到 404 以外的任何提示。
注解写在哪?控制器类还是方法?
ThinkPHP 的注解路由只识别写在控制器方法上的注解(如 @Get、@Post),类级别注解(如 @Middleware)不影响路由注册,但类本身必须继承 think\Controller 或使用 think\annotation\Route 手动声明命名空间前缀。
立即学习“PHP免费学习笔记(深入)”;
常见错误现象:
- 把
@Get("/api/user")写在类头部 → 路由不生效 - 方法没加
public修饰符 → 注解解析器跳过该方法 - 控制器没放在
app/controller/下,或命名空间与目录结构不匹配 → 自动扫描找不到
正确姿势:注解紧贴方法声明上方,方法为 public,且所在类能被框架自动加载。
@Route 和 @Get/@Post 的参数差异
@Route 是通用注解,支持完整配置;@Get、@Post 等是快捷注解,底层都转成 @Route。它们的关键区别在默认行为和可选参数范围。
比如:
@Get("user/:id")
public function read($id) { }
等价于:
@Route("user/:id", method="GET")
但如果你需要绑定中间件、设置域名或闭包验证,只能用 @Route:
@Route("admin/:id", method="GET", middleware="auth", domain="admin.example.com")
快捷注解不支持 middleware、domain、pattern 等字段,强行写进去会被忽略。
注解路由的性能与缓存问题
注解解析发生在运行时(首次请求或命令行扫描时),比配置式路由多一次反射和 AST 解析。TP8 默认开启注解缓存,但有个关键前提:文件修改后必须手动清缓存,否则旧注解仍生效。
容易踩的坑:
- 改完注解不执行
php think clear:route→ 新路由不出现,旧路由还残留 - 部署时没生成注解缓存(如 CI 环境未跑
php think build:annotation)→ 生产环境每次请求都重新解析,性能明显下降 - 使用 Swoole 或 RoadRunner 时,注解只在 Worker 启动时解析一次 → 修改注解后必须重启服务,热更无效
复杂点在于:它不像配置路由那样“改完即生效”,而是一套依赖缓存生命周期的解析机制,调试时得时刻分清当前看到的是缓存结果还是实时解析结果。











