依赖注入 #

一、依赖注入概述 #

1.1 什么是依赖注入 #

依赖注入是一种设计模式,将依赖从外部注入,而不是在类内部创建。

text
┌─────────────────────────────────────────────────────┐
│              依赖注入 vs 传统方式                    │
├─────────────────────────────────────────────────────┤
│                                                     │
│  传统方式:                                         │
│  class UserService {                                │
│      private $repository = new UserRepository();    │
│  }                                                  │
│                                                     │
│  依赖注入:                                         │
│  class UserService {                                │
│      public function __construct(                   │
│          private UserRepository $repository         │
│      ) {}                                           │
│  }                                                  │
│                                                     │
└─────────────────────────────────────────────────────┘

1.2 依赖注入优势 #

优势 说明
解耦 类不依赖具体实现
可测试 易于模拟依赖
灵活 可轻松替换实现
可维护 依赖关系清晰

二、构造函数注入 #

2.1 基本用法 #

php
<?php

namespace App\Service;

use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;

class UserService
{
    public function __construct(
        private EntityManagerInterface $entityManager,
        private UserRepository $userRepository,
        private LoggerInterface $logger
    ) {}
}

2.2 必需依赖 #

php
<?php

class OrderService
{
    public function __construct(
        private OrderRepository $orderRepository,
        private PaymentService $paymentService
    ) {
        if (!$orderRepository) {
            throw new \InvalidArgumentException('OrderRepository is required');
        }
    }
}

2.3 可选依赖 #

php
<?php

class NotificationService
{
    private ?LoggerInterface $logger;

    public function __construct(
        private MailerInterface $mailer,
        ?LoggerInterface $logger = null
    ) {
        $this->logger = $logger;
    }

    public function send(string $to, string $subject, string $body): void
    {
        $this->mailer->send($to, $subject, $body);
        
        $this->logger?->info("Email sent to $to");
    }
}

三、Setter注入 #

3.1 基本用法 #

php
<?php

namespace App\Service;

use Psr\Log\LoggerInterface;
use Symfony\Contracts\Service\Attribute\Required;

class EmailService
{
    private ?LoggerInterface $logger = null;

    #[Required]
    public function setLogger(LoggerInterface $logger): void
    {
        $this->logger = $logger;
    }

    public function send(string $to, string $content): void
    {
        $this->logger?->info("Sending email to $to");
    }
}

3.2 配置Setter注入 #

yaml
# config/services.yaml
services:
    App\Service\EmailService:
        calls:
            - setLogger: ['@logger']
            - setFromEmail: ['noreply@example.com']

四、属性注入 #

4.1 使用#[Required]属性 #

php
<?php

namespace App\Service;

use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Contracts\Service\Attribute\Required;

class ReportService
{
    #[Required]
    public LoggerInterface $logger;

    public function generate(): string
    {
        $this->logger->info('Generating report');
        return 'Report content';
    }
}

五、自动装配 #

5.1 启用自动装配 #

yaml
# config/services.yaml
services:
    _defaults:
        autowire: true
        autoconfigure: true

    App\:
        resource: '../src/'

5.2 自动装配原理 #

php
<?php

// 自动装配会根据类型提示自动注入
class UserService
{
    // 自动注入 UserRepository
    public function __construct(
        private UserRepository $userRepository
    ) {}
}

// 等同于手动配置
services:
    App\Service\UserService:
        arguments:
            $userRepository: '@App\Repository\UserRepository'

5.3 禁用自动装配 #

yaml
# config/services.yaml
services:
    App\Service\ManualService:
        autowire: false
        arguments:
            $dependency: '@App\Service\Dependency'

六、接口注入 #

6.1 接口定义 #

php
<?php

namespace App\Interface;

interface PaymentGatewayInterface
{
    public function charge(float $amount): bool;
    public function refund(string $transactionId): bool;
}

6.2 接口实现 #

php
<?php

namespace App\Service;

use App\Interface\PaymentGatewayInterface;

class StripeGateway implements PaymentGatewayInterface
{
    public function charge(float $amount): bool
    {
        return true;
    }

    public function refund(string $transactionId): bool
    {
        return true;
    }
}

class PayPalGateway implements PaymentGatewayInterface
{
    public function charge(float $amount): bool
    {
        return true;
    }

    public function refund(string $transactionId): bool
    {
        return true;
    }
}

6.3 配置接口别名 #

yaml
# config/services.yaml
services:
    App\Interface\PaymentGatewayInterface: '@App\Service\StripeGateway'

6.4 使用接口 #

php
<?php

class PaymentService
{
    public function __construct(
        private PaymentGatewayInterface $gateway
    ) {}

    public function processPayment(float $amount): bool
    {
        return $this->gateway->charge($amount);
    }
}

七、绑定参数 #

7.1 全局绑定 #

yaml
# config/services.yaml
services:
    _defaults:
        bind:
            $projectDir: '%kernel.project_dir%'
            $environment: '%kernel.environment%'
            $debug: '%kernel.debug%'

    App\Service\ConfigService:
        arguments:
            $siteName: '%app.site_name%'

7.2 使用绑定参数 #

php
<?php

namespace App\Service;

class FileService
{
    public function __construct(
        private string $projectDir,
        private string $environment,
        private bool $debug
    ) {}

    public function getUploadPath(): string
    {
        return $this->projectDir . '/var/uploads';
    }
}

八、#[Autowire]属性 #

8.1 注入服务 #

php
<?php

use Symfony\Component\DependencyInjection\Attribute\Autowire;

class UserService
{
    public function __construct(
        #[Autowire(service: 'logger')]
        private LoggerInterface $logger,
        
        #[Autowire('%kernel.project_dir%')]
        private string $projectDir
    ) {}
}

8.2 注入环境变量 #

php
<?php

class ApiService
{
    public function __construct(
        #[Autowire(env: 'API_KEY')]
        private string $apiKey,
        
        #[Autowire(env: 'API_TIMEOUT')]
        private int $timeout = 30
    ) {}
}

8.3 注入参数 #

php
<?php

class ConfigService
{
    public function __construct(
        #[Autowire('%app.items_per_page%')]
        private int $itemsPerPage,
        
        #[Autowire('%app.supported_locales%')]
        private array $supportedLocales
    ) {}
}

九、服务定位器 #

9.1 使用服务定位器 #

php
<?php

namespace App\Service;

use Symfony\Component\DependencyInjection\ServiceLocator;

class HandlerService
{
    public function __construct(
        private ServiceLocator $handlers
    ) {}

    public function handle(string $type): void
    {
        if ($this->handlers->has($type)) {
            $handler = $this->handlers->get($type);
            $handler->process();
        }
    }
}

9.2 配置服务定位器 #

yaml
# config/services.yaml
services:
    App\Service\HandlerService:
        arguments:
            $handlers:
                type1: '@App\Handler\Type1Handler'
                type2: '@App\Handler\Type2Handler'
                type3: '@App\Handler\Type3Handler'

十、惰性服务 #

10.1 配置惰性服务 #

yaml
# config/services.yaml
services:
    App\Service\HeavyService:
        lazy: true

10.2 使用惰性代理 #

php
<?php

use Symfony\Component\DependencyInjection\Attribute\AutowireLazy;

class ConsumerService
{
    public function __construct(
        #[AutowireLazy]
        private HeavyService $heavyService
    ) {}

    public function process(): void
    {
        // 只有实际调用时才初始化
        $this->heavyService->doSomething();
    }
}

十一、编译器传递 #

11.1 创建编译器传递 #

php
<?php

namespace App\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class CustomCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container): void
    {
        if (!$container->hasDefinition('app.handler_chain')) {
            return;
        }

        $definition = $container->getDefinition('app.handler_chain');
        
        $taggedServices = $container->findTaggedServiceIds('app.handler');

        foreach ($taggedServices as $id => $tags) {
            $definition->addMethodCall('addHandler', [$id]);
        }
    }
}

11.2 注册编译器传递 #

php
<?php

namespace App;

use App\DependencyInjection\Compiler\CustomCompilerPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class AppBundle extends Bundle
{
    public function build(ContainerBuilder $container): void
    {
        parent::build($container);
        
        $container->addCompilerPass(new CustomCompilerPass());
    }
}

十二、总结 #

本章学习了:

  • 依赖注入概念
  • 构造函数注入
  • Setter注入
  • 属性注入
  • 自动装配
  • 接口注入
  • 绑定参数
  • #[Autowire]属性
  • 服务定位器
  • 惰性服务
  • 编译器传递

下一章将学习 服务配置

最后更新:2026-03-28