# Injeção de dependências do Symfony: contêiner de serviços, autowiring e tags em 2026 > Mergulho na injeção de dependências do Symfony: contêiner de serviços, autowiring, atributo #[Autowire], ligação de interfaces, serviços com tags com #[AutowireIterator], compiler passes e serviços preguiçosos no Symfony 7.4 e 8.0. - Published: 2026-07-06 - Updated: 2026-07-07 - Author: SharpSkill - Tags: symfony, dependency injection, service container, autowiring, php, di - Reading time: 11 min --- A injeção de dependências do Symfony é a espinha dorsal de toda aplicação Symfony: o contêiner de serviços instancia, configura e conecta os objetos entre si, de modo que as classes declaram o que precisam em vez de construí-lo por conta própria. No Symfony 7.4 e 8.0, o autowiring, os atributos do PHP e os serviços com tags reduziram a configuração explícita a quase nada, mantendo o contêiner totalmente resolvido e previsível em tempo de compilação. Entender como essa resolução funciona separa os desenvolvedores que lutam contra o contêiner daqueles que o deixam fazer o trabalho. > **O que é injeção de dependências no Symfony?** > > Injeção de dependências no Symfony é um padrão em que o contêiner de serviços passa as dependências de um objeto pelo construtor em vez de deixar o objeto criá-las. O autowiring resolve automaticamente cada argumento tipado para um serviço correspondente, de modo que a maioria das classes não exige nenhuma configuração manual. ## Como o contêiner de serviços do Symfony resolve as dependências O contêiner de serviços é uma classe PHP compilada que sabe construir cada serviço da aplicação. Durante a fase de compilação do contêiner, o Symfony analisa as definições de classes, lê as assinaturas dos construtores e produz uma fábrica em cache. Em tempo de execução, solicitar um serviço retorna uma instância compartilhada e construída de forma preguiçosa. Como a ligação acontece em tempo de compilação, uma dependência mal configurada falha quando o cache é construído, e não quando uma requisição chega à produção. Um serviço é simplesmente uma classe que executa uma tarefa e é gerenciada pelo contêiner. O arquivo `config/services.yaml` padrão registra tudo o que está sob `src/` como serviço, com autowiring e autoconfiguração habilitados, e é por isso que uma classe comum funciona sem nenhum ajuste extra. ```yaml # config/services.yaml services: _defaults: autowire: true # inject arguments by type-hint autoconfigure: true # apply tags based on implemented interfaces App\: resource: '../src/' exclude: - '../src/Entity/' - '../src/Kernel.php' ``` Com esses padrões no lugar, uma classe pede seus colaboradores pelo construtor e o contêiner os fornece. Sem fábrica, sem `new`, sem lista de argumentos escrita à mão. ```php // src/Notification/SlackNotifier.php namespace App\Notification; use Psr\Log\LoggerInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; final class SlackNotifier { // The container reads each type-hint and injects a matching service public function __construct( private readonly HttpClientInterface $httpClient, private readonly LoggerInterface $logger, ) {} public function send(string $channel, string $message): void { $this->httpClient->request('POST', 'https://slack.example/api/post', [ 'json' => ['channel' => $channel, 'text' => $message], ]); $this->logger->info('Slack message dispatched', ['channel' => $channel]); } } ``` O contêiner instancia `SlackNotifier` uma única vez, injeta nele o cliente HTTP e o logger compartilhados e reutiliza essa instância em todo lugar onde ela é solicitada. Esse comportamento compartilhado por padrão é o que torna o contêiner rápido e econômico em memória ao mesmo tempo. O modelo completo está documentado no [guia oficial do contêiner de serviços do Symfony](https://symfony.com/doc/current/service_container.html), que se apoia no padrão da [interface de contêiner PSR-11](https://www.php-fig.org/psr/psr-11/) compartilhado por todo o ecossistema PHP. O compartilhamento pode ser desativado quando uma nova instância é exigida por requisição, por exemplo um builder com estado que acumula dados. Definir `shared: false` em uma definição a transforma em uma fábrica que retorna um objeto novo a cada injeção. Isso é raro na prática, pois a maioria dos serviços são colaboradores sem estado, mas saber que esse padrão existe e como sobrescrevê-lo importa quando um bug remonta a dois consumidores que mutam por engano o mesmo objeto. ## Autowiring do Symfony: type hints e o atributo Autowire O autowiring do Symfony resolve os argumentos do construtor pela declaração de tipo. Quando um argumento é tipado com uma classe ou interface que corresponde a exatamente um serviço, o contêiner o injeta sem nenhuma configuração. O autowiring só desiste diante de valores que não são serviços: escalares, variáveis de ambiente e parâmetros. O atributo `#[Autowire]` fecha essa lacuna diretamente sobre o argumento, mantendo a ligação junto ao código que a usa. ```php // src/Service/PaymentGateway.php namespace App\Service; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; final class PaymentGateway { public function __construct( // Inject a container parameter with the %...% syntax #[Autowire('%kernel.environment%')] private readonly string $environment, // Inject an environment variable, resolved at runtime #[Autowire(env: 'STRIPE_SECRET')] private readonly string $apiKey, // Bind a specific service when the type alone is ambiguous #[Autowire(service: 'monolog.logger.payment')] private readonly LoggerInterface $logger, ) {} } ``` Cada argumento declara a própria origem: um parâmetro, uma variável de ambiente ou um serviço nomeado. O atributo também aceita um argumento `expression:` para valores de Expression Language, útil quando uma dependência vem de um cálculo em tempo de execução em vez de uma definição estática. Manter esses metadados no construtor permite que quem lê veja exatamente de onde nasce cada valor sem abrir um arquivo YAML à parte. A referência do atributo está na [documentação do autowiring do Symfony](https://symfony.com/doc/current/service_container/autowiring.html). > **Tipe interfaces, não classes concretas** > > Fazer autowiring de uma classe concreta como `S3Storage` acopla cada consumidor àquela implementação e torna dolorosa sua substituição nos testes. Tipe a interface em vez disso e deixe `#[AsAlias]` ou `#[Autowire(service: ...)]` decidir o serviço concreto. Injetar o contêiner inteiro para buscar serviços na mão anula as verificações em tempo de compilação e é justamente o antipadrão que o autowiring existe para eliminar. ## Ligando interfaces quando existem várias implementações O autowiring contra uma interface funciona de forma limpa até que duas classes a implementem. Nesse ponto o contêiner não consegue adivinhar qual injetar e lança um erro de ambiguidade em tempo de compilação. Dois atributos resolvem isso: `#[AsAlias]` marca uma implementação como a padrão para a interface, e `#[Autowire(service: ...)]` fixa uma implementação específica em um argumento individual. ```php // src/Storage/StorageInterface.php namespace App\Storage; interface StorageInterface { public function put(string $key, string $contents): void; } ``` ```php // src/Storage/S3Storage.php namespace App\Storage; use Symfony\Component\DependencyInjection\Attribute\AsAlias; // Becomes the default service returned for StorageInterface #[AsAlias(StorageInterface::class)] final class S3Storage implements StorageInterface { public function put(string $key, string $contents): void { /* upload to S3 */ } } ``` ```php // src/Service/BackupService.php namespace App\Service; use App\Storage\LocalStorage; use App\Storage\StorageInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; final class BackupService { public function __construct( // Resolves to the #[AsAlias] default (S3Storage) private readonly StorageInterface $primary, // Pins the concrete implementation explicitly by service id #[Autowire(service: LocalStorage::class)] private readonly StorageInterface $fallback, ) {} } ``` O atributo `#[Target]` oferece uma terceira opção: referencia uma implementação pelo nome do seu alias de autowiring, desacoplando o nome da variável do argumento da resolução. Escolher entre esses mecanismos depende da intenção. Use `#[AsAlias]` quando uma implementação for genuinamente a padrão em toda a base de código, e `#[Autowire(service: ...)]` ou `#[Target]` quando um único consumidor precisar de uma variante não padrão. Esse padrão aparece o tempo todo em aplicações de produção, e é um tema predileto do [módulo de entrevista sobre injeção de dependências do Symfony](/technologies/symfony/interview-questions/dependency-injection). ## Tags de serviços do Symfony e o coletor AutowireIterator As tags são a forma como o Symfony agrupa serviços relacionados para que outro serviço consuma todos de uma vez. Isso alimenta os padrões strategy, cadeia de responsabilidade e plugin que percorrem o próprio framework: as extensões de tipo de formulário, as extensões do Twig e os event subscribers são todos guiados por tags. Em vez de aplicar tags manualmente, `#[AutoconfigureTag]` colocado sobre uma interface diz ao contêiner para aplicar tag automaticamente a cada classe que a implementa. ```php // src/Export/ExporterInterface.php namespace App\Export; use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; // Every class implementing this interface is tagged automatically #[AutoconfigureTag('app.exporter')] interface ExporterInterface { public function supports(string $format): bool; public function export(array $rows): string; } ``` Cada implementação controla sua posição na coleção com `#[AsTaggedItem]`, que define uma prioridade (a mais alta executa primeiro) e um índice de string opcional para acesso por chave. ```php // src/Export/CsvExporter.php namespace App\Export; use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem; // Higher priority services appear earlier in the injected iterable #[AsTaggedItem(priority: 10)] final class CsvExporter implements ExporterInterface { public function supports(string $format): bool { return $format === 'csv'; } public function export(array $rows): string { return implode("\n", array_map(fn ($r) => implode(',', $r), $rows)); } } ``` O serviço consumidor reúne cada exporter com tag em um `iterable` ordenado usando `#[AutowireIterator]`. Em seguida percorre a coleção e delega à primeira estratégia que suporta o formato solicitado. ```php // src/Export/ExportManager.php namespace App\Export; use Symfony\Component\DependencyInjection\Attribute\AutowireIterator; final class ExportManager { // Collects all services tagged 'app.exporter', ordered by priority public function __construct( #[AutowireIterator('app.exporter')] private readonly iterable $exporters, ) {} public function export(string $format, array $rows): string { foreach ($this->exporters as $exporter) { if ($exporter->supports($format)) { return $exporter->export($rows); } } throw new \InvalidArgumentException("No exporter for format: $format"); } } ``` Quando a coleção é grande ou cara de construir, `#[AutowireLocator]` é a alternativa preguiçosa: injeta um contêiner PSR-11 que instancia um serviço com tag somente quando ele é de fato buscado pela chave. Essa distinção entre iteração ansiosa e busca preguiçosa importa para serviços que mantêm conexões ou estado pesado. ## Autoconfiguração, compiler passes e serviços preguiçosos A autoconfiguração é o mecanismo que lê atributos como `#[AutoconfigureTag]` e `#[AsTaggedItem]` e os aplica durante a compilação. É por isso que implementar `EventSubscriberInterface` basta para registrar um event subscriber sem nenhuma configuração. Para as transformações que os atributos não conseguem expressar, um compiler pass se engancha diretamente na construção do contêiner e muta as definições antes de o cache ser escrito. O código-fonte do componente `DependencyInjection` no [GitHub](https://github.com/symfony/dependency-injection) mostra como o próprio framework usa internamente dezenas dessas passes. Grafos sensíveis a desempenho se beneficiam dos serviços preguiçosos. Marcar um argumento com `#[Autowire(lazy: true)]` adia a instanciação até a primeira chamada da dependência, apoiado nos objetos preguiçosos nativos do PHP 8.4 em vez de uma biblioteca de proxies gerados. ```php // src/Service/ReportBuilder.php namespace App\Service; use App\Pdf\PdfRenderer; use Symfony\Component\DependencyInjection\Attribute\Autowire; final class ReportBuilder { public function __construct( // PdfRenderer is only instantiated on first actual use #[Autowire(lazy: true)] private readonly PdfRenderer $renderer, ) {} public function buildMonthly(): string { // If no method touches $this->renderer, it is never constructed return $this->renderer->render('monthly-report'); } } ``` Os objetos preguiçosos nativos chegaram com o PHP 8.4 e são tratados em profundidade no guia sobre [Symfony 8 e os objetos preguiçosos do PHP 8.4](/blog/symfony/symfony-8-new-features-php-84-lazy-objects). A recompensa é um contêiner que se mantém enxuto: colaboradores pesados como geradores de PDF, clientes de SDK de terceiros ou geradores de relatórios não custam nada até que o caminho de código que os exige de fato execute. ## Perguntas e respostas de entrevista sobre DI no Symfony Uma pergunta de entrevista comum sobre DI no Symfony pede a diferença entre autowiring e autoconfiguração. O autowiring resolve os argumentos do construtor de um serviço específico pelo tipo, enquanto a autoconfiguração aplica tags, chamadas de método e ajustes a um serviço conforme as interfaces que ele implementa. São dois interruptores independentes: um serviço pode ter autowiring sem estar autoconfigurado, e vice-versa. Outra pergunta frequente é por que o contêiner distingue serviços públicos e privados. Os serviços são privados por padrão, o que significa que não podem ser recuperados diretamente do contêiner com `$container->get()` e só podem ser injetados. Serviços privados podem ser inlinados e eliminados por otimização durante a compilação, o que é mais rápido; serviços públicos precisam permanecer recuperáveis, então o otimizador os deixa intactos. Recuperar serviços manualmente do contêiner é tratado como antipadrão justamente porque anula essa otimização e esconde as dependências. Dependências circulares são uma armadilha clássica e uma pergunta de acompanhamento frequente. Se o serviço A precisa de B no construtor e B precisa de A, o contêiner não consegue construir nenhum dos dois e reporta uma referência circular em tempo de compilação. As correções padrão são quebrar o ciclo extraindo o comportamento compartilhado para um terceiro serviço, mudar um lado para injeção preguiçosa com `#[Autowire(lazy: true)]` para que a dependência seja resolvida apenas na primeira chamada, ou recorrer à injeção por setter para que o objeto exista antes de seu colaborador ser ligado. Reconhecer por que o ciclo é um defeito de design, e não apenas como silenciá-lo, é o que os entrevistadores realmente escutam. Os entrevistadores também sondam o momento da resolução do contêiner. Como o Symfony compila o contêiner uma vez e o mantém em cache, uma dependência irresolvível surge durante o aquecimento do cache em vez de no momento da requisição. Essa garantia é uma consequência direta do modelo em tempo de compilação e uma das razões pelas quais grandes aplicações Symfony permanecem previsíveis sob carga. Para um conjunto mais amplo dessas perguntas, a [trilha da tecnologia Symfony](/technologies/symfony) as agrupa por componente e dificuldade. ## Conclusão - Deixe o contêiner trabalhar: registre as classes sob `src/`, tipe as dependências no construtor e evite por completo a ligação manual em YAML. - Recorra a `#[Autowire]` apenas para não-serviços, parâmetros, variáveis de ambiente e identificadores de serviço específicos. - Resolva a ambiguidade de interfaces com `#[AsAlias]` para o padrão e `#[Autowire(service: ...)]` ou `#[Target]` para escolhas explícitas. - Conduza os padrões strategy e plugin com `#[AutoconfigureTag]`, as prioridades de `#[AsTaggedItem]` e `#[AutowireIterator]`; troque por `#[AutowireLocator]` quando a busca preguiçosa for preferível à iteração ansiosa. - Adie colaboradores caros com `#[Autowire(lazy: true)]`, que usa os objetos preguiçosos nativos do PHP 8.4. - Mantenha os serviços privados e injetados em vez de recuperados, para que o compilador possa inliná-los e otimizar o grafo. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pt/blog/symfony/symfony-dependency-injection-service-container-autowiring-tags