Laravel 13 服务容器进阶

服务容器是 Laravel 的核心,提供了强大的依赖注入和服务管理能力。本文将深入探讨 Laravel 13 服务容器的高级用法。

服务容器基础

绑定服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use App\Services\PaymentService;
use App\Contracts\PaymentGateway;

// 简单绑定
app()->bind(PaymentService::class, function ($app) {
return new PaymentService();
});

// 单例绑定
app()->singleton(PaymentService::class, function ($app) {
return new PaymentService(config('payment'));
});

// 实例绑定
$service = new PaymentService();
app()->instance(PaymentService::class, $service);

解析服务

1
2
3
4
5
6
7
8
9
10
use App\Services\PaymentService;

// 使用 app 辅助函数
$service = app(PaymentService::class);

// 使用 resolve 辅助函数
$service = resolve(PaymentService::class);

// 使用 make 方法
$service = app()->make(PaymentService::class);

绑定类型

简单绑定

1
2
3
4
5
6
7
8
app()->bind('service', function ($app) {
return new Service();
});

// 每次解析都创建新实例
$service1 = app('service');
$service2 = app('service');
// $service1 !== $service2

单例绑定

1
2
3
4
5
6
7
8
app()->singleton('service', function ($app) {
return new Service();
});

// 只创建一次实例
$service1 = app('service');
$service2 = app('service');
// $service1 === $service2

作用域单例

1
2
3
4
5
app()->scoped('service', function ($app) {
return new Service();
});

// 在同一个请求/生命周期内是单例

实例绑定

1
2
$service = new Service();
app()->instance('service', $service);

接口绑定

接口到实现

1
2
3
4
use App\Contracts\PaymentGateway;
use App\Services\StripeGateway;

app()->bind(PaymentGateway::class, StripeGateway::class);

条件绑定

1
2
3
4
5
6
7
8
9
10
11
use App\Contracts\PaymentGateway;
use App\Services\StripeGateway;
use App\Services\PaypalGateway;

app()->bind(PaymentGateway::class, function ($app) {
if (config('payment.provider') === 'stripe') {
return new StripeGateway();
}

return new PaypalGateway();
});

上下文绑定

when 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
use App\Contracts\PaymentGateway;
use App\Services\StripeGateway;
use App\Services\PaypalGateway;
use App\Controllers\OrderController;
use App\Controllers\SubscriptionController;

app()->when(OrderController::class)
->needs(PaymentGateway::class)
->give(StripeGateway::class);

app()->when(SubscriptionController::class)
->needs(PaymentGateway::class)
->give(PaypalGateway::class);

给定值

1
2
3
app()->when(OrderController::class)
->needs('$paymentProvider')
->give('stripe');

给定闭包

1
2
3
4
5
app()->when(OrderController::class)
->needs(PaymentGateway::class)
->give(function ($app) {
return new StripeGateway(config('stripe'));
});

标签绑定

定义标签

1
2
3
4
app()->tag([
StripeGateway::class,
PaypalGateway::class,
], 'payment-gateways');

解析标签

1
2
3
4
5
$gateways = app()->tagged('payment-gateways');

foreach ($gateways as $gateway) {
$gateway->process();
}

自动解析

构造函数注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

namespace App\Services;

use App\Contracts\PaymentGateway;

class OrderService
{
public function __construct(
protected PaymentGateway $gateway
) {}
}

// 自动解析
$orderService = app(OrderService::class);

方法注入

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

namespace App\Http\Controllers;

use App\Services\OrderService;

class OrderController extends Controller
{
public function store(OrderService $service)
{
$service->process();
}
}

服务提供者

创建服务提供者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Contracts\PaymentGateway;
use App\Services\StripeGateway;

class PaymentServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->bind(PaymentGateway::class, StripeGateway::class);

$this->app->singleton('payment', function ($app) {
return new PaymentService($app->make(PaymentGateway::class));
});
}

public function boot(): void
{
// 注册发布配置
$this->publishes([
__DIR__.'/../../config/payment.php' => config_path('payment.php'),
]);
}
}

注册服务提供者

1
2
3
4
5
// config/app.php
'providers' => [
App\Providers\AppServiceProvider::class,
App\Providers\PaymentServiceProvider::class,
],

延迟提供者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class PaymentServiceProvider extends ServiceProvider
{
protected $defer = true;

public function register(): void
{
$this->app->singleton('payment', function ($app) {
return new PaymentService();
});
}

public function provides(): array
{
return ['payment'];
}
}

解析高级用法

带参数解析

1
2
$service = app()->makeWith(Service::class, ['param' => 'value']);
$service = app()->make(Service::class, ['param' => 'value']);

容器调用

1
2
3
4
5
6
7
use App\Services\OrderService;

$result = app()->call([OrderService::class, 'process']);

$result = app()->call(function (OrderService $service) {
return $service->process();
});

带参数调用

1
2
3
4
5
6
7
$result = app()->call([OrderService::class, 'process'], [
'orderId' => 123,
]);

$result = app()->call(function (OrderService $service, $orderId) {
return $service->process($orderId);
}, ['orderId' => 123]);

容器事件

解析事件

1
2
3
4
5
6
7
app()->resolving(function ($object, $app) {
// 每次解析时调用
});

app()->resolving(PaymentService::class, function ($service, $app) {
// 解析 PaymentService 时调用
});

afterResolving

1
2
3
app()->afterResolving(function ($object, $app) {
// 解析完成后调用
});

容器扩展

extend 方法

1
2
3
4
app()->extend(Service::class, function ($service, $app) {
$service->setLogger($app->make(LoggerInterface::class));
return $service;
});

依赖注入

控制器注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

namespace App\Http\Controllers;

use App\Services\UserService;

class UserController extends Controller
{
public function __construct(
protected UserService $userService
) {}

public function index()
{
return $this->userService->getAll();
}
}

中间件注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php

namespace App\Http\Middleware;

use Closure;
use App\Services\AnalyticsService;

class TrackPageView
{
public function __construct(
protected AnalyticsService $analytics
) {}

public function handle($request, Closure $next)
{
$this->analytics->track($request);
return $next($request);
}
}

命令注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Services\ReportService;

class GenerateReport extends Command
{
protected $signature = 'report:generate';

public function __construct(
protected ReportService $reportService
) {
parent::__construct();
}

public function handle(): int
{
$this->reportService->generate();
return 0;
}
}

测试服务容器

模拟绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use App\Services\PaymentService;

public function test_payment_service(): void
{
$mock = $this->mock(PaymentService::class, function ($mock) {
$mock->shouldReceive('process')->once()->andReturn(true);
});

$this->app->instance(PaymentService::class, $mock);

$response = $this->postJson('/api/orders');

$response->assertStatus(201);
}

交换实例

1
2
3
4
5
6
7
8
9
10
11
use App\Services\PaymentService;

public function test_with_fake_service(): void
{
$fake = new FakePaymentService();
$this->app->instance(PaymentService::class, $fake);

$response = $this->postJson('/api/orders');

$this->assertTrue($fake->wasCalled());
}

最佳实践

1. 使用接口绑定

1
2
3
4
5
// 好的做法
app()->bind(PaymentGatewayInterface::class, StripeGateway::class);

// 不好的做法
app()->bind('payment', StripeGateway::class);

2. 使用服务提供者

1
2
3
4
5
6
7
8
9
10
11
// 好的做法:在服务提供者中注册
class PaymentServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->bind(PaymentGateway::class, StripeGateway::class);
}
}

// 不好的做法:在代码中直接绑定
app()->bind(PaymentGateway::class, StripeGateway::class);

3. 使用类型提示

1
2
3
4
5
6
7
8
9
10
// 好的做法
public function __construct(
protected PaymentService $paymentService
) {}

// 不好的做法
public function __construct()
{
$this->paymentService = app(PaymentService::class);
}

总结

Laravel 13 的服务容器提供了强大而灵活的依赖注入能力。通过合理使用绑定、上下文绑定、标签和服务提供者,可以构建出松耦合、可测试的应用程序。记住使用接口绑定来实现依赖反转原则,在服务提供者中注册服务,并通过构造函数注入来获取依赖。服务容器是 Laravel 的核心,理解它的工作原理对于构建高质量的 Laravel 应用至关重要。