laravel 6 服务容器的 bind() 配合闭包实现工厂模式:注册时不执行,仅在 make() 或自动注入时延迟调用闭包创建新实例,支持动态配置、依赖解析和运行时参数,每次调用均生成独立对象,体现“按需生产”本质。

Laravel 6 的服务容器通过 bind() 方法配合闭包(Closure),实现类的动态、按需、延迟实例化,这正是工厂模式的核心表现。它不提前创建对象,而是在真正需要时(比如调用 app()->make() 或自动注入时)才执行闭包,生成新实例。
服务容器中工厂闭包的本质
工厂闭包就是一个返回具体类实例的匿名函数,由你完全控制构造逻辑。容器只负责“调用它”,不干涉内部如何 new、传参、判断条件或复用已有对象。
例如:
$this->app->bind('PaymentGateway', function ($app) {
$driver = config('payment.driver'); // 动态读配置
if ($driver === 'alipay') {
return new AlipayGateway(config('payment.alipay'));
}
return new WechatGateway(config('payment.wechat'));
});
这里 $app 是容器实例,可用来解析其他依赖;闭包返回的是全新对象,每次 make('PaymentGateway') 都会重新执行该闭包 → 实现了“工厂动态创建”。
关键实现要点
bind()绑定闭包即注册工厂
容器把闭包存进$bindings数组,不执行,只登记。make()触发闭包执行
当调用app()->make('PaymentGateway'),容器查到绑定的闭包,传入$app和可选参数,call_user_func()执行它,拿到返回值作为实例。闭包内可自由访问容器、配置、请求上下文等
比如在闭包里用$app->make(UserRepository::class)注入依赖,或request()->ip()获取当前请求信息,实现运行时决策。不是单例,除非你显式用
singleton()或在闭包里自己缓存
默认bind()每次make()都新建 —— 正是工厂模式“每次产出新实例”的体现。
常见工厂场景写法
-
基于环境切换实现类
$this->app->bind(LoggerInterface::class, function ($app) { return app()->environment('local') ? new FileLogger(storage_path('logs/local.log')) : new SyslogLogger(); }); -
带运行时参数的工厂(需配合
make()传参)$this->app->bind('ReportGenerator', function ($app, $parameters) { return new PdfReportGenerator($parameters['format'], $parameters['theme']); }); // 使用时: app()->make('ReportGenerator', ['format' => 'a4', 'theme' => 'dark']); -
封装为独立工厂类再绑定
$this->app->bind('NotificationSender', function ($app) { return new NotificationFactory($app)->create(config('notification.channel')); });
注意事项
- 闭包不能含未序列化对象(如
Request实例),因 Laravel 6 的队列/缓存若需持久化闭包,得靠laravel/serializable-closure(PHP ≥7.4 支持)。 - 若闭包里用了
$this,需用use ($this)显式捕获,但通常不推荐 —— 工厂逻辑应保持无状态、可测试。 -
bind()和singleton()都接受闭包,区别仅在于是否缓存结果:singleton()第一次执行后把返回值存进$instances,后续直接返回;bind()总是重执行。
不复杂但容易忽略:工厂闭包的价值不在“能写代码”,而在把对象创建逻辑从业务代码中剥离,让控制器、服务类彻底不用关心“谁来 new、怎么 new、用哪个配置”。容器成了统一的、可配置的、可测试的对象生产线。











