PHP项目Symfony依赖注入容器

wen PHP项目 3

Symfony 依赖注入容器详解

Symfony 的依赖注入容器是其最核心的组件之一,它实现了"控制反转"(IoC)设计模式。

PHP项目Symfony依赖注入容器

基本概念

依赖注入:将对象的依赖关系从内部创建转移到外部注入

// 不好的做法 - 硬编码依赖
class UserController {
    private $repository;
    public function __construct() {
        $this->repository = new UserRepository(); // 紧耦合
    }
}
// 好的做法 - 依赖注入
class UserController {
    private $repository;
    public function __construct(UserRepository $repository) {
        $this->repository = $repository; // 松耦合
    }
}

服务容器核心服务

// 1. 注册服务(services.yaml)
services:
    # 默认自动注册
    App\Service\:
        resource: '../src/Service/*'
        autowire: true
        autoconfigure: true
    # 手动注册服务
    App\Service\MailService:
        arguments:
            $mailer: '@Symfony\Component\Mailer\MailerInterface'
            $adminEmail: 'admin@example.com'
    # 注册接口实现
    App\Repository\UserRepositoryInterface:
        alias: App\Repository\DoctrineUserRepository
        public: true

服务容器核心组件

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
class CustomContainerConfiguration
{
    public function configure()
    {
        $container = new ContainerBuilder();
        // 1. 定义服务
        $container->register('app.mailer', MailerService::class)
            ->setArguments([
                new Reference('Symfony\Component\Mailer\MailerInterface'),
                '%admin_email%'
            ])
            ->setPublic(true);
        // 2. 设置参数
        $container->setParameter('admin_email', 'admin@example.com');
        // 3. 设置标签
        $container->register('app.event_listener', EventListener::class)
            ->addTag('kernel.event_listener', ['event' => 'kernel.response']);
        return $container;
    }
}

高级特性

1 自动装配 (Autowiring)

// config/services.yaml
services:
    _defaults:
        autowire: true      # 自动注入
        autoconfigure: true # 自动配置
        public: false       # 默认私有
    App\:
        resource: '../src/*'
        exclude: '../src/{DependencyInjection,Entity,Migrations,Tests}'
// 控制器中使用
class ProductController extends AbstractController
{
    private $productService;
    private $entityManager;
    // Symfony 会自动注入这些依赖
    public function __construct(
        ProductService $productService,
        EntityManagerInterface $entityManager
    ) {
        $this->productService = $productService;
        $this->entityManager = $entityManager;
    }
}

2 服务标签 (Service Tags)

// 自定义标签
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class CustomCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $taggedServices = $container->findTaggedServiceIds('app.processor');
        foreach ($taggedServices as $id => $tags) {
            // 处理标签服务
            $definition = $container->getDefinition($id);
            $definition->addMethodCall('setLogger', [
                new Reference('logger')
            ]);
        }
    }
}
// 服务定义添加标签
services:
    App\Service\PdfProcessor:
        tags:
            - { name: 'app.processor', priority: 10, format: 'pdf' }

3 服务装饰器 (Service Decoration)

// 装饰原有服务
services:
    App\Service\OriginalMailer:
        class: App\Service\MailerService
    App\Decorator\LoggingMailer:
        decorates: App\Service\OriginalMailer
        decoration_inner_name: 'app.original_mailer'
        arguments:
            - '@App\Decorator\LoggingMailer.inner'
    # 或者使用接口装饰
    App\Decorator\CachingMailer:
        decorates: App\Service\MailerService
        decoration_on_invalid: exception
        arguments:
            - '@.inner'

4 容器编译优化

// 自定义编译器
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class PerformanceCompilerPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        // 1. 优化自动加载
        foreach ($container->getDefinitions() as $id => $definition) {
            if ($definition->isPublic()) {
                $definition->setPublic(false);
                $container->setAlias($id, $definition->getClass());
            }
        }
        // 2. 预编译表达式
        $container->getParameterBag()->resolve();
        // 3. 优化服务延迟加载
        foreach ($container->getDefinitions() as $definition) {
            $definition->setPublic(false);
            if (!$definition->isLazy()) {
                $definition->setLazy(true);
            }
        }
    }
}

实际应用示例

1 复杂依赖注入

// services.yaml
services:
    App\Service\OrderService:
        arguments:
            $connection: '@database_connection'
            $eventDispatcher: '@event_dispatcher'
            $logger: '@monolog.logger.order'
        calls:
            - ['setCache', ['@cache.app']]
        tags:
            - { name: 'monolog.logger', channel: 'order' }
// 注入配置参数
parameters:
    orders.payment.timeout: 3600
    orders.shipping.methods: ['standard', 'express', 'next_day']
services:
    App\Service\PaymentService:
        arguments:
            $config:
                timeout: '%orders.payment.timeout%'
                methods: '%orders.shipping.methods%'

2 动态服务创建

// 工厂模式
class ServiceFactory
{
    public static function create(string $type): ServiceInterface
    {
        switch ($type) {
            case 'email':
                return new EmailService();
            case 'sms':
                return new SmsService();
            default:
                throw new \InvalidArgumentException();
        }
    }
}
// services.yaml
services:
    App\Service\NotificationService:
        factory: ['App\Factory\ServiceFactory', 'create']
        arguments:
            - '%notification_type%'

性能优化技巧

// 1. 参数注入优化
services:
    _defaults:
        bind:
            string $projectDir: '%kernel.project_dir%'
// 2. 服务懒加载
services:
    App\Service\ExpensiveService:
        lazy: true  # 惰性加载直到使用时才实例化
// 3. 共享服务 vs 非共享服务
services:
    App\Service\SharedService:
        shared: true  # 单例模式(默认)
    App\Service\NonSharedService:
        shared: false # 每次获取新实例
// 4. 常量注入
services:
    App\Service\EnvAwareService:
        arguments:
            $env: !php/const App\Kernel::ENVIRONMENT
            $debug: '%kernel.debug%'

容器调试命令

# 显示所有服务
php bin/console debug:container
# 显示特定服务详情
php bin/console debug:container App\Service\MailService
# 显示服务参数
php bin/console debug:container --parameters
# 显示服务标签
php bin/console debug:container --tags
# 显示自动装配服务
php bin/console debug:autowiring

最佳实践建议

// 1. 优先使用构造器注入
class UserService {
    public function __construct(
        private UserRepository $repository,
        private LoggerInterface $logger
    ) {}
}
// 2. 避免循环依赖
// 坏示例
class A { public function __construct(B $b) {} }
class B { public function __construct(A $a) {} } // 循环依赖!
// 好 - 使用事件或服务定位器
class A {
    public function __construct(EventDispatcherInterface $dispatcher) {}
}
// 3. 使用依赖注入而不是服务定位器
// 避免这样做
class BadClass {
    public function doSomething() {
        $container = $this->getContainer();
        $service = $container->get('app.service');
    }
}
// 应该这样做
class GoodClass {
    public function __construct(
        private MyService $service
    ) {}
}

Symfony 的 DI 容器提供了强大而灵活的服务管理机制,正确使用可以显著提升代码的可维护性和可测试性。

抱歉,评论功能暂时关闭!