service类本身不自动执行,只在被调用时运行;它是普通php类,需显式实例化或容器解析,生命周期由开发者控制,常见于控制器、路由闭包、事件监听器中,不可在全局中间件依赖未解析的$request->param()。

Service类本身不自动执行,只在被调用时才运行
ThinkPHP 中的 Service 不是生命周期中的一个自动触发阶段,它没有“执行时机”这个概念——它只是普通 PHP 类,和 Model、Repository 一样,必须显式实例化或通过容器解析后被调用。框架不会在路由、中间件、控制器之外“额外执行”某个 Service。
常见误解是把 Service 当成类似中间件或事件监听器那样有注册即生效的组件,其实不是。它的生命周期完全由开发者控制。
Service通常在控制器或事件回调里被调用
绝大多数情况下,Service 的逻辑是在控制器方法中触发的,比如:
class OrderController extends BaseController
{
public function create()
{
$service = app()->make(OrderService::class);
$result = $service->createOrder($this->request->param());
return json($result);
}
}
也可以在以下位置使用:
- 路由闭包中:
Route::get('pay', function () { return app(PayService::class)->handle(); }); - 事件监听器里(如监听
order.created):app(OrderNotificationService::class)->send(); - 中间件中(但需谨慎:全局中间件里 Request 尚未完成参数解析,
$request->param()为空)
Service::boot() 和 Service::register() 是服务提供者的方法,不是 Service 实例的方法
容易混淆的一点:你在 app/provider.php 或插件的 think-service 声明里写的 register() 和 boot(),属于服务提供者(ServiceProvider),不是你定义的业务 Service 类的方法。
它们的执行时机是明确的:
-
register():在应用初始化阶段($app->initialize())执行,用于绑定接口到实现,比如$this->app->bind('PayService', PayService::class) -
boot():在所有服务注册完成后、路由匹配前执行,适合做依赖注入后的初始化,比如监听事件、配置默认值
你自定义的 PayService 类里写个 boot() 方法,框架根本不会调它——除非你自己在某处手动调用。
别在全局中间件里直接 new Service 并依赖 Request 参数
这是高频翻车点。例如在 app/middleware.php 定义的全局中间件中写:
class AuthMiddleware
{
public function handle($request, \Closure $next)
{
$user = (new UserService())->getCurrentUser($request->param('token')); // ❌ 错误
return $next($request);
}
}
问题在于:此时 Route::check() 还没执行,$request->param() 返回空数组,token 拿不到。正确做法是用原始 Server 变量:
- 改用
$request->server('HTTP_AUTHORIZATION')或$request->header('authorization') - 或者只做基础校验(如签名校验、IP 白名单),把用户身份解析留到控制器或 Service 内部
Service 的职责边界要清晰:它不该承担请求解析工作,那是 Request 和路由的事。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











